chore: drop the flat content mirror and native install support (ADR-0024)
apm becomes the only supported install path. The flat mirror at each plugin root existed solely so Claude Code's native `claude plugin install` could convention-scan plugin content (ADR-0017). With no native consumers, it cost ~20,000 tracked lines plus ~2,100 lines of sync tooling and ~88s of every push to guard content apm never reads — and its only automated gate, `claude plugin validate --strict`, passes on a plugin with zero content, so it could not detect the defect ADR-0017 was created to fix. Removes the mirror (213 files), the six per-plugin manifest pairs, sync-plugin-content.sh, its 1,289-line test, the orphaned marketplace-plugins.sh, and the check-plugin-content-sync and validate-plugins pre-push hooks. The root `marketplace:` block and .claude-plugin/ catalogue stay: apm's own marketplace consumers read that same file, so `<name>@holocron` short names keep working. tests/run-bats.sh now excludes .claude/skills/. apm installs from .apm/, which carries the tests/ dirs the mirror stripped, so deployed .bats files would otherwise be discovered and double-run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "gitea",
|
||||
"version": "1.3.8",
|
||||
"description": "Skills and agents for working with a Gitea forge through its HTTP API \u2014 the forge's own objects, as distinct from the local git clone.",
|
||||
"author": {
|
||||
"name": "Defame1297",
|
||||
"email": "defame1297@rkdr.net",
|
||||
"url": "https://git.dev.rkdr.net/Defame1297/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/gitea",
|
||||
"repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/gitea",
|
||||
"keywords": [
|
||||
"gitea",
|
||||
"issues",
|
||||
"prs",
|
||||
"milestones",
|
||||
"releases",
|
||||
"branches"
|
||||
]
|
||||
}
|
||||
21
plugins/gitea/.github/plugin/plugin.json
vendored
21
plugins/gitea/.github/plugin/plugin.json
vendored
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "gitea",
|
||||
"version": "1.3.8",
|
||||
"description": "Skills and agents for working with a Gitea forge through its HTTP API \u2014 the forge's own objects, as distinct from the local git clone.",
|
||||
"author": {
|
||||
"name": "Defame1297",
|
||||
"email": "defame1297@rkdr.net",
|
||||
"url": "https://git.dev.rkdr.net/Defame1297/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/gitea",
|
||||
"repository": "https://git.dev.rkdr.net/Defame1297/holocron/src/branch/main/plugins/gitea",
|
||||
"keywords": [
|
||||
"gitea",
|
||||
"issues",
|
||||
"prs",
|
||||
"milestones",
|
||||
"releases",
|
||||
"branches"
|
||||
]
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
name: gitea-orchestrate
|
||||
|
||||
description: Orchestrates Gitea operations for other agents. Invoke when a caller needs a multi-step or destructive Gitea operation (merge a PR, delete a branch/release/tag/label/milestone, delete a file) coordinated across domain skills with safety gates, session context, and structured results.
|
||||
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
|
||||
disallowedTools: Edit, Write, NotebookEdit
|
||||
---
|
||||
|
||||
You are the orchestrator for the gitea plugin — a composable workflow dispatcher designed for other agents to invoke multi-step Gitea operations reliably. Your one job is routing and safety-gating: you do not call `mcp__gitea__*` tools yourself, you delegate to domain skills and enforce confirmation on destructive operations. You never edit files. Every write you cause reaches its target through a domain skill's Gitea API call — never through an edit you make to the local working tree.
|
||||
|
||||
You resolve `owner`/`repo` once per session (via `rtk git remote -v` on `origin`) and carry that forward as session context to every domain skill you dispatch to, rather than making each skill re-resolve it.
|
||||
|
||||
**Scope:** this orchestrator routes Gitea-object operations across the six domain skills only: `gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`. `gitea-workflow` is also not routed here, but for a different reason than a missing domain: it is a human-facing conversational wrapper that gives status check-ins and resolves ambiguous bare numbers ("what's going on with #42") by reasoning about phrasing and context, and it composes the same six domain skills directly rather than calling this orchestrator. It is not a peer to invoke instead of this dispatcher — agent callers route Gitea-object operations here directly with an explicit `operation` field; direct human users to `gitea-workflow` when they want guided, conversational help. Never invoke `gitea-workflow` as an agent caller — resolve ambiguous issue/PR numbers yourself (see Number resolution below) instead of relying on its conversational disambiguation.
|
||||
|
||||
## Hard rules
|
||||
|
||||
These are non-negotiable regardless of `confirm` or any skill-local override:
|
||||
- Never delete the repository's default branch (typically `main` or `master`) — refused outright, independent of `confirm`.
|
||||
- `delete_release` takes a numeric `id`; `delete_tag` takes a `tag_name` string. These are asymmetric and never interchangeable — resolve the correct identifier via `list_releases`/`get_release` before calling either, and never guess one from the other.
|
||||
- `rename-branch` is gated like a delete even though it destroys nothing: what a rename does to open PRs using the branch as head or base, to a matching protection rule, and to every other clone's tracking branch is unconfirmed by `gitea-branches`' sources. Require `confirm: true`, and verify the PR and protection sides afterwards.
|
||||
- Deleting a release does not delete its tag, and vice versa — if the caller's intent is to remove both, dispatch both operations explicitly rather than assuming one implies the other.
|
||||
- A 404 from any domain skill does not necessarily mean the target doesn't exist — Gitea hides permission errors as not-found. Surface this ambiguity in the error `code` (`not_found_or_forbidden`) rather than reporting a hard "does not exist."
|
||||
- Label and milestone IDs must be resolved via `gitea-labels-milestones` before being applied to an issue or PR — never pass a label/milestone name directly to `gitea-issues`/`gitea-prs`, they require numeric IDs.
|
||||
- Issues and PRs share one number space. Before dispatching an operation keyed on a bare number, resolve whether it's an issue or a PR yourself (see Number resolution) — never infer the domain from operation phrasing alone.
|
||||
- `list_releases`/`list_tags` default to `per_page: 20` (other domains default to 30) with no server-side auto-pagination — when a caller needs a complete result set, loop `page` upward until a page returns fewer than `per_page` results before returning.
|
||||
- Never commit secrets, credentials, or environment-specific config into any file written via `gitea-files`.
|
||||
- You are read-only against the local working tree. Never create, edit, or delete a local file — not a manifest, not a config, not a scratch note. Local state is the caller's, and you only read it (e.g. `rtk git remote -v`) to resolve context.
|
||||
|
||||
### Number resolution
|
||||
|
||||
When an operation targets a bare issue/PR number and the caller hasn't specified which domain it is:
|
||||
1. Dispatch to `gitea-issues` with `issue_read method: "get"` on that number.
|
||||
2. Check the response's `is_pull` field: `true` → re-dispatch to `gitea-prs` for the actual operation; `false`/absent → it's an issue, proceed with `gitea-issues`.
|
||||
3. Cache the resolution in session context for the remainder of the request so repeated references to the same number don't re-resolve.
|
||||
4. If the resolution call 404s, do not conclude the number doesn't exist — return `not_found_or_forbidden` and suggest verifying token scope (`write:issue`).
|
||||
|
||||
Sub-skills carry their own local copies of relevant gotchas for humans who invoke them directly, bypassing this orchestrator. When a caller routes through you, this section is the enforcement backstop: check every routed operation against it before dispatch, not just the destructive-operation confirm gate below.
|
||||
|
||||
When invoked, you:
|
||||
1. Parse the incoming workflow request (operation type, parameters, context overrides)
|
||||
2. Check safety gates: if the operation is destructive (rename-branch, delete-branch, delete-release, delete-tag, delete-label, delete-milestone, delete-file, merge-pr) and the request lacks explicit `confirm: true`, fail immediately with "requires explicit confirmation"; deleting the default branch is refused outright regardless of `confirm`
|
||||
3. Route to the appropriate domain skill: `gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-branches`, `gitea-files`, `gitea-releases`
|
||||
4. Manage session context: resolve and carry forward `owner`/`repo` and any cached number-space resolutions, passing them explicitly to each skill
|
||||
5. Handle error recovery: for recoverable failures (rate limiting, transient 5xx, pagination gaps) retry or complete the operation; for ambiguous 404s, attempt the permission-vs-not-found disambiguation before failing
|
||||
6. Aggregate results and return structured JSON output suitable for agent chaining
|
||||
|
||||
## Inputs
|
||||
|
||||
- **operation:** string, one of:
|
||||
- issues: list-issues, get-issue, create-issue, update-issue, comment-issue, search-issues
|
||||
- labels/milestones: list-labels, create-label, update-label, delete-label, list-milestones, create-milestone, update-milestone, close-milestone, delete-milestone, resolve-labels
|
||||
- prs: list-prs, get-pr, create-pr, update-pr, close-pr, reopen-pr, merge-pr, review-pr
|
||||
- branches/commits: list-branches, create-branch, rename-branch, delete-branch, list-commits, get-commit
|
||||
- files: get-file, get-dir, get-tree, write-file, delete-file
|
||||
- releases/tags: list-releases, get-release, create-release, delete-release, list-tags, create-tag, delete-tag
|
||||
- **parameters:** object, operation-specific arguments (issue/PR number, title, body, label names, tag name, file path, etc.)
|
||||
- **context:** object (optional), session state to carry forward (`owner`, `repo`, cached number-space resolutions)
|
||||
- **confirm:** boolean (optional), explicit confirmation for destructive operations (required if not set for rename-branch, delete-branch, delete-release, delete-tag, delete-label, delete-milestone, delete-file, merge-pr)
|
||||
|
||||
## Process
|
||||
|
||||
1. Validate the request structure and check if `operation` is known
|
||||
2. Check the request against the Hard rules above (default-branch deletion, release/tag id-vs-name asymmetry, label/milestone ID resolution, number-space ambiguity, pagination) — refuse outright on violation, independent of `confirm`
|
||||
3. If destructive operation: require `confirm: true`, else fail with structured "requires explicit confirmation" error
|
||||
4. Resolve `owner`/`repo` via `rtk git remote -v` on `origin` if not already present in `context`, and reuse the resolution for the remainder of the request
|
||||
5. If the operation targets a bare number and the domain isn't specified, run Number resolution above before dispatch
|
||||
6. Invoke the appropriate domain skill via `Skill` with the operation, parameters, and resolved context (`owner`, `repo`)
|
||||
7. Catch and handle Gitea errors: disambiguate 404s (not-found vs. permission-hidden), retry transient failures, loop pagination for `list_releases`/`list_tags` until exhausted
|
||||
8. If recovery succeeds, continue; if not, return error structure with diagnostics and suggestions. If the blocker looks trivially fixable by a local edit — a stale `origin` URL, a malformed config, a missing label the repo obviously wants — name that fix in `suggestions` and stop. Do not act on it, and do not route it as a write operation the caller never asked for
|
||||
9. Aggregate all outputs and return as structured JSON
|
||||
|
||||
## Output
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success" | "error",
|
||||
"operation": "<operation_name>",
|
||||
"result": {
|
||||
"output": "<domain skill output or result>",
|
||||
"context": { "owner": "...", "repo": "...", "resolved_number_type": "issue" | "pull" | null },
|
||||
"applied_config": { "confirm_required": true | false }
|
||||
},
|
||||
"error": {
|
||||
"message": "<human-readable error>",
|
||||
"code": "<error type: not_found_or_forbidden | conflict | auth_failure | invalid_state | pagination_incomplete>",
|
||||
"recovery_attempted": true | false,
|
||||
"suggestions": ["<suggestion1>", "<suggestion2>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
name: gitea-branches
|
||||
|
||||
description: >
|
||||
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 rename_branch, list_commits, and get_commit. Requires git remote "origin" pointing to the Gitea instance.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
version: "0.1.3"
|
||||
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__rename_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **404 can mean 403.** Gitea masks permission errors as not-found — check token scope before reporting a branch or commit missing.
|
||||
- **Nothing auto-paginates.** `list_branches`/`list_commits` return one page — iterate `page` until the 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
|
||||
|
||||
Extract `owner` and `repo` from the git remote before any tool call — `get_me` and `list_my_repos` are blocked under this skill's token scope, so the remote is the only source:
|
||||
|
||||
```bash
|
||||
rtk git remote get-url origin
|
||||
```
|
||||
|
||||
No origin, or not a Gitea URL: stop and report "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
| Invocation | Action |
|
||||
|---|---|
|
||||
| `/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/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/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, applying the 404 gotcha above before reporting "not found" to the user.
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
topic: branches
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Branch operations
|
||||
|
||||
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`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `page` (number, optional, default: `1`)
|
||||
- `per_page` (number, optional, default: `30`)
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
list_branches owner: <owner> repo: <repo>
|
||||
```
|
||||
|
||||
**Response:** one object per branch: `name`, `protected` (bool), `commit_sha` (present when the
|
||||
underlying commit data is available).
|
||||
|
||||
Paginate for the full list (see Gotchas) — iterate `page` until the count is less than `per_page`.
|
||||
|
||||
## `create_branch`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `branch` (string, required) — new branch name
|
||||
- `old_branch` (string, optional) — source branch; if omitted, defaults to the repo's default
|
||||
branch server-side (not necessarily your current local checkout)
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
create_branch owner: <owner> repo: <repo> branch: <new-name> old_branch: <source-branch>
|
||||
```
|
||||
|
||||
Default dispatch: if the user gives a base ("branch off of X", "from X"), pass it as `old_branch`.
|
||||
If they don't specify a base and you're mid-task on a local branch, pass your current branch
|
||||
(`rtk git branch --show-current`) as `old_branch` so the new branch forks from where you're actually
|
||||
working, rather than silently falling back to the repo default. If neither applies (e.g. a fresh
|
||||
top-level request with no working branch context), omit `old_branch` and let it default server-side.
|
||||
|
||||
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:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `branch` (string, required)
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
delete_branch owner: <owner> repo: <repo> branch: <name>
|
||||
```
|
||||
|
||||
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
|
||||
protected, every time, regardless of how the request is phrased.
|
||||
|
||||
## Token scope
|
||||
|
||||
`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.
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
topic: commits
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# 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`, 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
|
||||
shares any tool family with branch create/delete.
|
||||
|
||||
## `list_commits`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `sha` (string, optional) — starting SHA or branch name; if omitted, gitea-mcp uses the repo's
|
||||
default branch
|
||||
- `path` (string, optional) — restrict results to commits that touched this file/path
|
||||
- `page` (number, optional, default: `1`, minimum: `1`)
|
||||
- `per_page` (number, optional, default: `30`, minimum: `1`)
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
list_commits owner: <owner> repo: <repo> sha: <branch-or-sha> path: <optional-path>
|
||||
```
|
||||
|
||||
Dispatch defaults:
|
||||
- "commits on `<branch>`" → pass `<branch>` as `sha`.
|
||||
- "commits touching `<path>`" (no branch mentioned) → pass `path` alone, `sha` omitted (defaults to
|
||||
the repo's default branch).
|
||||
- Both given → pass both; the result is history for that path, walked from that branch/SHA.
|
||||
- Neither given → omit both; this returns default-branch history, which is a reasonable default for
|
||||
an open-ended "what's the recent history here" question.
|
||||
|
||||
**Response:** one object per commit: `sha`, `html_url`, `created`, `message` (when available),
|
||||
`author` (`{name, email, date}`, when available).
|
||||
|
||||
Paginate per the pagination Gotcha in SKILL.md if you need more than one page of history.
|
||||
|
||||
## `get_commit`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `sha` (string, required)
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
get_commit owner: <owner> repo: <repo> sha: <commit-sha>
|
||||
```
|
||||
|
||||
**Response:** same shape as a `list_commits` entry, but always fully populated (`message` and
|
||||
`author` are guaranteed present, not conditional). Use this when the user asks about one specific
|
||||
commit by SHA rather than browsing history — `list_commits` entries may omit `message`/`author` in
|
||||
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 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`).
|
||||
@@ -1,41 +0,0 @@
|
||||
# Sources
|
||||
|
||||
**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 — **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.
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **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`
|
||||
|
||||
## 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, 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`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 — informed the protected-branch gotcha in SKILL.md (protected branches can block server-side operations regardless of client-side checks; admins aren't exempt by default).
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI docs on Context7 — practitioner conventions for issues, PRs, and releases (semver tags, draft/prerelease flags). Consulted as part of the shared research pass but its content is scoped to releases/tags, out of scope for branches/commits — no content from it was used in this skill.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
name: gitea-files
|
||||
|
||||
description: >
|
||||
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
|
||||
is not actually required for any of this domain's five tools.
|
||||
|
||||
metadata:
|
||||
version: "1.0.1"
|
||||
category: gitea
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
|
||||
allowed-tools: mcp__gitea__get_file_contents mcp__gitea__get_dir_contents mcp__gitea__get_repository_tree mcp__gitea__create_or_update_file mcp__gitea__delete_file
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **404 may mean an under-scoped token, not a missing path.** These tools gate on `write:repository`; check scope before concluding the path is missing.
|
||||
- **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.
|
||||
|
||||
## Inputs
|
||||
|
||||
`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.
|
||||
|
||||
## Dispatch
|
||||
|
||||
Read the reference for the row you land on before making the call.
|
||||
|
||||
| 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 |
|
||||
|
||||
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,49 +0,0 @@
|
||||
---
|
||||
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 doesn't hold every entry — page with
|
||||
`page`/`per_page` (defaults `1`/`30`) until a page returns fewer 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`; an under-scoped token gets 404 instead of 403 so the
|
||||
endpoint doesn't leak whether the resource exists. On a path you're confident about, check token
|
||||
scope before concluding it doesn't exist.
|
||||
@@ -1,48 +0,0 @@
|
||||
# Sources
|
||||
|
||||
## 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. 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 — 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
|
||||
|
||||
**Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for files (top-level `sha`, no nested `content.sha`) and directory/tree entries.
|
||||
|
||||
**Source:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:**
|
||||
- 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
|
||||
|
||||
**Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — confirms direct-commit file editing through the web UI is a first-class, expected workflow rather than an API-only escape hatch.
|
||||
|
||||
**Source:** context7:/websites/gitea
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Handoff — committing straight to a branch is the normal path, not an escape hatch)
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
**Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner command patterns for issues, PRs, and releases, including semver tag/release conventions, draft/prerelease flags, and release-notes-from-file conventions. Consulted alongside `context7-websites-gitea` while researching `workflow-conventions.md` (both sources contribute to that research doc, backing `gitea-workflow`); its file-command patterns did not end up informing any gitea-files content.
|
||||
|
||||
**Source:** context7:/git_gitea_com/gitea_tea
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
- **Contributing files:** (none)
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
name: gitea-issues
|
||||
|
||||
description: >
|
||||
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
|
||||
when invoked directly by a human; an orchestrating caller (e.g. gitea-workflow) may pass
|
||||
owner/repo already resolved.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
version: "0.1.5"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
|
||||
allowed-tools: Bash mcp__gitea__list_issues mcp__gitea__issue_read mcp__gitea__issue_write mcp__gitea__search_issues
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`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.
|
||||
- **404 may mean 403.** Gitea hides permission errors as not-found — check `write:issue` scope before concluding the issue doesn't exist.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
Skip if an orchestrating caller already passed `owner`/`repo` in, or the action is `search` (cross-repository, needs only a query). Otherwise, before any tool call:
|
||||
|
||||
```bash
|
||||
rtk git remote get-url origin
|
||||
```
|
||||
|
||||
No origin, or not a Gitea URL: stop and report "No Gitea remote found — set origin to your Gitea instance URL."
|
||||
|
||||
## Step 2 — Dispatch
|
||||
|
||||
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.
|
||||
|
||||
| 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 — Create
|
||||
|
||||
Only the create flow reaches this step.
|
||||
|
||||
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
|
||||
|
||||
Reads: a compact table or numbered list — number, title, state, labels, milestone.
|
||||
|
||||
Writes: what was created or updated, with the issue number and the URL when one is returned.
|
||||
|
||||
Errors: the HTTP code and message as returned, without paraphrasing either.
|
||||
@@ -1,91 +0,0 @@
|
||||
---
|
||||
topic: enrichments
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
---
|
||||
|
||||
# Create-flow enrichments
|
||||
|
||||
Closes out the four enrichments deferred from issue #6 comment #848. Run in this order before the
|
||||
`issue_write method: "create"` call in SKILL.md's create dispatch: **labels → milestone →
|
||||
assignee → dependency link**. Each step is independent and skippable on its own — a missing
|
||||
milestone fit doesn't block label inference, and so on — but this is the order that lets later
|
||||
steps use context established earlier (e.g. the inferred `Kind/*` label can inform which milestone
|
||||
plausibly fits).
|
||||
|
||||
**Cross-skill composition note:** none of the steps below read `gitea-labels-milestones`'s
|
||||
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.
|
||||
|
||||
## 1. Label inference
|
||||
|
||||
Delegate the entire signal-to-label mapping to `gitea-labels-milestones` — this skill does not
|
||||
duplicate the `Kind/*`/`Priority/*`/`Status/*` taxonomy table.
|
||||
|
||||
1. Compose `gitea-labels-milestones` to resolve labels for the issue being created: give it the
|
||||
draft title/body and ask it to infer and resolve applicable labels (it calls
|
||||
`label_read method: "list_repo_labels"` internally and runs its own inference procedure).
|
||||
2. Take back the resolved label IDs (and which scope groups, if any, need replacing — not relevant
|
||||
yet on a brand-new issue, since there's nothing to replace).
|
||||
3. If `gitea-labels-milestones` reports low confidence and omits a `Kind/*` label, pass no `Kind/*`
|
||||
ID rather than guessing one yourself. `Priority/Medium` is the one label its own inference
|
||||
procedure defaults to when no urgency signal is present — that's expected, not a gap.
|
||||
4. Pass the resulting label IDs to `issue_write`'s `labels` parameter (omit the parameter entirely
|
||||
if the resolved list is empty).
|
||||
|
||||
## 2. Milestone assignment on create
|
||||
|
||||
1. Compose `gitea-labels-milestones` to list open milestones (it calls
|
||||
`milestone_read method: "list" state: "open"` internally).
|
||||
2. Compare the issue's inferred scope (title, body, and any `Kind/*` label from step 1) against
|
||||
each open milestone's title/description. Assign a milestone only when the fit is clear — a
|
||||
milestone literally named for the feature area, or one whose description explicitly covers this
|
||||
kind of work. A milestone that's merely "the current one" without a clear scope match is not a
|
||||
confident fit.
|
||||
3. If a milestone clearly fits, pass its **numeric ID** (never the title) as `issue_write`'s
|
||||
`milestone` parameter. `issue_write`'s `milestone` field only accepts the ID — see
|
||||
`references/issues.md`.
|
||||
4. If no milestone clearly fits, omit `milestone` entirely. Guessing a milestone assignment is worse
|
||||
than leaving it unset — an issue can always be milestoned later, but a wrong milestone
|
||||
assignment pollutes that milestone's issue count and scope.
|
||||
|
||||
## 3. Assignee on create — the `get_me` workaround
|
||||
|
||||
`issue_write` accepts `assignees: [<login>]`, but there is no way to discover the *current user's*
|
||||
own login to self-assign: `get_me` requires the `read:user` scope, and this repo's Gitea MCP token is
|
||||
scoped to `write:issue` + `write:repository` only, with no `read:user` grant. This is a hard
|
||||
capability gap, not something to work around with a guess.
|
||||
|
||||
**Workaround:** support an optional user-configured default assignee login, supplied one of two
|
||||
ways:
|
||||
- A config value the caller or orchestrator (e.g. `gitea-workflow`) already resolved and passes in
|
||||
when invoking this skill.
|
||||
- A login explicitly stated in the conversation ("assign this to alice") — use that login directly,
|
||||
no lookup needed, since Gitea accepts a login string without requiring you to resolve an ID first.
|
||||
|
||||
**If neither is available, omit `assignees` entirely.** Do not guess a login, do not fail the create
|
||||
over a missing assignee, and do not attempt `get_me`/`search_users` as a fallback — both are blocked
|
||||
by the same scope gap and will only produce a confusing secondary error.
|
||||
|
||||
## 4. Dependency-linking convention
|
||||
|
||||
gitea-mcp has no native issue-dependency field (no "blocks"/"blocked by" relationship in the API
|
||||
surface this skill has access to). The convention is to write **"Depends on #N"** as a line in the
|
||||
issue body.
|
||||
|
||||
This is not just a text convention with no effect — Gitea auto-renders `#N` (and `!N` for PRs) as a
|
||||
real clickable cross-reference with no separate API call, a documented platform behavior (see
|
||||
`references/sources.md` for the backing research). This works because issues and PRs share one
|
||||
repo-scoped number space. Use the bare `#N` form for same-repo dependencies; use `owner/repo#N` for
|
||||
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"`. No separate field or follow-up call is
|
||||
involved — the rendering happens automatically once the body is saved.
|
||||
@@ -1,136 +0,0 @@
|
||||
---
|
||||
topic: issues
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Issue operations
|
||||
|
||||
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`
|
||||
|
||||
**Parameters (live schema):**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `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`)
|
||||
|
||||
**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:**
|
||||
```text
|
||||
list_issues owner: <owner> repo: <repo> state: "open" type: "issues"
|
||||
```
|
||||
|
||||
**Response (list item):** `number`, `title`, `state`, `html_url`, `user`, `comments`, `created_at`,
|
||||
`updated_at`, and optionally `labels` (`[]string`), `milestone` (`{id, title}`), `ref`, `deadline`.
|
||||
Body and `closed_at` are omitted from list responses — call `issue_read method: "get"` for those.
|
||||
|
||||
Paginate: `page`/`per_page`, stop once the count is below `per_page`.
|
||||
|
||||
## `issue_read`
|
||||
|
||||
**Parameters (live schema, matches `api-reference.md`):**
|
||||
- `method` (string, required, enum) — `"get"` | `"get_comments"` | `"get_labels"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `issue_number` (number, required)
|
||||
|
||||
**`get`** — full issue: `number`, `title`, `body`, `state`, `html_url`, `user`, `labels`
|
||||
(`[]string`), `comments`, `created_at`, `updated_at`, `closed_at`, and optionally `assignees`
|
||||
(`[]string`), `milestone` (`{id, title}`), `ref`, `deadline`, `is_pull` (present only when this
|
||||
number is backed by a pull request — absent, not `false`, on true issues).
|
||||
|
||||
**`get_comments`** — array of `{id, body, user, html_url, created_at, updated_at}`.
|
||||
|
||||
**`get_labels`** — array of full label objects (`id`, `name`, `color`, `description` — not slimmed
|
||||
to name strings, unlike the labels array on `get`).
|
||||
|
||||
**Call:**
|
||||
```text
|
||||
issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>
|
||||
```
|
||||
|
||||
Always check `is_pull` before treating a number as a plain issue — see the shared number-space
|
||||
gotcha in SKILL.md.
|
||||
|
||||
## `issue_write`
|
||||
|
||||
**Parameters (live schema, matches `api-reference.md`):**
|
||||
- `method` (string, required, enum) — `"create"` | `"update"` | `"add_comment"` | `"edit_comment"` |
|
||||
`"add_labels"` | `"remove_label"` | `"replace_labels"` | `"clear_labels"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `issue_number` (number, required for every method except `"create"`)
|
||||
- `title` (string, required for `"create"`)
|
||||
- `body` (string, required for `"create"`, `"add_comment"`, `"edit_comment"`)
|
||||
- `assignees` (array of strings, optional) — login names (see `references/enrichments.md` for why
|
||||
this is usually omitted)
|
||||
- `milestone` (number, optional) — milestone ID, never a title
|
||||
- `state` (string, enum `"open"`/`"closed"`/`"all"`, optional) — for `"update"`
|
||||
- `commentID` (number, optional, required for `"edit_comment"`)
|
||||
- `labels` (array of numbers, optional) — label IDs, never names — for `add_labels`/`replace_labels`
|
||||
- `label_id` (number, optional, required for `"remove_label"`) — singular, not the array form
|
||||
- `ref` (string, optional) — branch association, informational only
|
||||
- `deadline` (string, optional) — ISO 8601
|
||||
- `remove_deadline` (boolean, optional)
|
||||
|
||||
**Create:**
|
||||
```text
|
||||
issue_write method: "create"
|
||||
owner: <owner> repo: <repo>
|
||||
title: <title> body: <body>
|
||||
labels: [<resolved IDs>] ← omit if none confidently inferred
|
||||
milestone: <resolved ID> ← omit if none clearly fits
|
||||
assignees: ["<login>"] ← omit if no default configured
|
||||
```
|
||||
|
||||
**Close:**
|
||||
```text
|
||||
issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed"
|
||||
```
|
||||
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
|
||||
`write:issue` + `write:repository`.
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
topic: search
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# `search_issues`
|
||||
|
||||
Call signature verified live against the deployed `gitea-mcp` server via `ToolSearch` at authoring
|
||||
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) — 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
|
||||
- `page` (number, optional, default `1`)
|
||||
- `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"
|
||||
```
|
||||
|
||||
This is a cross-repository search (unlike `list_issues`, which is scoped to one `owner`/`repo`) —
|
||||
useful when the caller doesn't know which repo an issue lives in, or wants results across an
|
||||
organization. Pass `owner` to narrow scope if the caller does know it.
|
||||
|
||||
Paginate the same way as `list_issues`: iterate `page` until the returned count is less than
|
||||
`per_page`.
|
||||
@@ -1,42 +0,0 @@
|
||||
# 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` 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
|
||||
|
||||
- **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.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/issues.md, references/search.md, references/enrichments.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go
|
||||
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for issues (label name-vs-ID slimming, milestone object-vs-string shape, `is_pull` presence on single-item reads only).
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/issues.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — backs the automatic cross-reference rendering (`#N`/`!N`) that validates the "Depends on #N" dependency-linking convention.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** references/enrichments.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner command patterns for issues, PRs, and releases. Consulted alongside context7-websites-gitea while researching `workflow-conventions.md`'s cross-reference-linking section (both sources contribute to that research doc); its issue-specific command patterns did not end up informing any gitea-issues content beyond what context7-websites-gitea already backs.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
name: gitea-labels-milestones
|
||||
|
||||
description: >
|
||||
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.
|
||||
|
||||
metadata:
|
||||
category: integration
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
version: "0.1.6"
|
||||
|
||||
allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Applying a label needs a numeric ID; issue/PR responses give only name strings.** Resolve name → ID with `label_read` first.
|
||||
- **`pull_request_read` returns `milestone` as a bare title string, `issue_read` as `{id, title}`.** Recover the ID by listing milestones and matching the title.
|
||||
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's `exclusive` field.** `list_repo_labels` always returns it; `label_write` can only set it via org create methods ("org only"). `true` means Gitea enforces one-per-scope; replacing instead of stacking on a `false` label destroys a valid one.
|
||||
|
||||
## Step 1 — Resolve owner, repo and org
|
||||
|
||||
Extract `owner` and `repo` from the git remote before any tool call (skip if an orchestrating caller already passed them in):
|
||||
|
||||
```bash
|
||||
rtk git remote get-url origin
|
||||
```
|
||||
|
||||
No origin, or 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's the org name whenever the owner is an organisation, which the remote URL doesn't say.
|
||||
|
||||
Read the failure text before interpreting it: `list_org_labels` needs `read:organization`, which this skill's declared scopes don't carry, so it fails with a scope error *before* it ever checks org-vs-user. Report the unchecked scope, not an empty pool — only a genuine not-found means 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"` |
|
||||
| Create a repo/org label | `label_write` | `"create_repo_label"` / `"create_org_label"` |
|
||||
| Edit a repo/org label | `label_write` | `"edit_repo_label"` / `"edit_org_label"` |
|
||||
| Delete a repo/org label | `label_write` | `"delete_repo_label"` / `"delete_org_label"` |
|
||||
| List milestones | `milestone_read` | `"list"` |
|
||||
| Get one milestone by ID | `milestone_read` | `"get"` |
|
||||
| Create a milestone | `milestone_write` | `"create"` |
|
||||
| Update / close a milestone | `milestone_write` | `"update"` |
|
||||
| Delete a milestone | `milestone_write` | `"delete"` |
|
||||
|
||||
Every list method paginates manually (`per_page` default 30) — iterate `page: 1, 2, ...` until a page returns fewer than `per_page`. A truncated list silently breaks name → ID resolution.
|
||||
|
||||
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`.
|
||||
@@ -1,81 +0,0 @@
|
||||
---
|
||||
topic: label-inference
|
||||
source_keys:
|
||||
- context7-websites-gitea
|
||||
- gitea-mcp-repo
|
||||
---
|
||||
|
||||
# Label inference guide
|
||||
|
||||
Maps context-pattern signals from conversation content (an issue being drafted, a bug report, a PR
|
||||
description) to this repo's `Kind/*` / `Priority/*` / `Status/*` label taxonomy. Used by
|
||||
`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.
|
||||
|
||||
## Branching on exclusivity
|
||||
|
||||
`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
|
||||
|
||||
**`Kind/*`** (what kind of work this is):
|
||||
|
||||
| Signal in context | Label |
|
||||
|---|---|
|
||||
| Bug report, error, crash, unexpected behavior, "broken", "doesn't work" | `Kind/Bug` |
|
||||
| New capability, "add support for", net-new functionality | `Kind/Feature` |
|
||||
| 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):
|
||||
|
||||
| Signal in context | Label |
|
||||
|---|---|
|
||||
| "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):
|
||||
|
||||
| Signal in context | Label |
|
||||
|---|---|
|
||||
| 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 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, 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.
|
||||
@@ -1,122 +0,0 @@
|
||||
---
|
||||
topic: labels
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
---
|
||||
|
||||
# Label operations
|
||||
|
||||
Execution detail for `label_read` and `label_write`. Both tools operate on either **repo-scoped**
|
||||
or **org-scoped** labels — never both in one call. Pick the method family (`*_repo_label*` vs.
|
||||
`*_org_label*`) that matches the target, and pass `owner`+`repo` or `org` accordingly.
|
||||
|
||||
## Verified live schemas
|
||||
|
||||
`label_read` — required: `method`.
|
||||
|
||||
| Param | Type | Notes |
|
||||
|---|---|---|
|
||||
| `method` | string (enum) | `"list_repo_labels"` \| `"get_repo_label"` \| `"list_org_labels"` |
|
||||
| `owner` | string | for repo methods |
|
||||
| `repo` | string | for repo methods |
|
||||
| `org` | string | for org methods |
|
||||
| `id` | number | label ID, required for `"get_repo_label"` |
|
||||
| `page` | number | default `1` |
|
||||
| `per_page` | number | default `30` |
|
||||
|
||||
`label_write` — required: `method`.
|
||||
|
||||
| Param | Type | Notes |
|
||||
|---|---|---|
|
||||
| `method` | string (enum) | `"create_repo_label"` \| `"edit_repo_label"` \| `"delete_repo_label"` \| `"create_org_label"` \| `"edit_org_label"` \| `"delete_org_label"` |
|
||||
| `owner` | string | for repo methods |
|
||||
| `repo` | string | for repo methods |
|
||||
| `org` | string | for org methods |
|
||||
| `id` | number | for edit/delete |
|
||||
| `name` | string | required for create |
|
||||
| `color` | string | hex `#RRGGBB`, required for create |
|
||||
| `description` | string | optional |
|
||||
| `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
|
||||
either label tool — only `method` is. Passing none for a repo/org method still fails, just as a
|
||||
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 count is below `per_page` — the only way to build a complete
|
||||
name → ID map, since there's 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
|
||||
|
||||
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"
|
||||
color: "#d73a4a"
|
||||
description: "Confirmed bug"
|
||||
```
|
||||
|
||||
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. `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"
|
||||
```
|
||||
|
||||
Only pass the fields being changed — `id` plus any of `name`/`color`/`description`/`is_archived`.
|
||||
|
||||
## Delete a label
|
||||
|
||||
```text
|
||||
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
Deleting a label does not remove it from historical issue/PR timeline events — it disappears only
|
||||
from current label lists.
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
topic: milestones
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Milestone operations
|
||||
|
||||
Execution detail for `milestone_read` and `milestone_write`. Milestones are always repo-scoped —
|
||||
there is no org-level milestone concept, unlike labels.
|
||||
|
||||
## Verified live schemas
|
||||
|
||||
`milestone_read` — required: `method`, `owner`, `repo`.
|
||||
|
||||
| Param | Type | Notes |
|
||||
|---|---|---|
|
||||
| `method` | string (enum) | `"get"` \| `"list"` |
|
||||
| `owner` | string | required |
|
||||
| `repo` | string | required |
|
||||
| `id` | number | milestone ID, required for `"get"` |
|
||||
| `name` | string | title filter, for `"list"` |
|
||||
| `state` | string | default `"all"` — conventional values `"open"`/`"closed"`/`"all"`, but **not enforced by an enum in the live schema** (plain string). Any other value is passed through to Gitea rather than rejected client-side. |
|
||||
| `page` | number | default `1` |
|
||||
| `per_page` | number | default `30` |
|
||||
|
||||
`milestone_write` — required: `method`, `owner`, `repo`.
|
||||
|
||||
| Param | Type | Notes |
|
||||
|---|---|---|
|
||||
| `method` | string (enum) | `"create"` \| `"update"` \| `"edit"` \| `"delete"` — `"update"`/`"edit"` are aliases for the same operation; prefer `"update"` |
|
||||
| `owner` | string | required |
|
||||
| `repo` | string | required |
|
||||
| `id` | number | required for update/delete |
|
||||
| `title` | string | required for create |
|
||||
| `description` | string | optional |
|
||||
| `due_on` | string | due date — the live tool schema only describes this as an opaque "due date" string with no enforced format; ISO 8601 (e.g. `"2025-03-01T00:00:00Z"`) is the conventional value Gitea's REST API accepts, not something confirmed by the live MCP schema itself |
|
||||
| `state` | string (enum) | `"open"` \| `"closed"` — **this one is schema-enforced**, unlike `milestone_read`'s `state` |
|
||||
|
||||
Note: unlike `label_read`/`label_write`, both milestone tools hard-require `owner` and `repo` at the
|
||||
schema level — there's no scope variant to omit them for.
|
||||
|
||||
## List milestones
|
||||
|
||||
```text
|
||||
milestone_read method: "list" owner: <owner> repo: <repo> state: "open"
|
||||
```
|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
## Resolve a milestone ID from a title
|
||||
|
||||
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>
|
||||
```
|
||||
|
||||
and take the `id` of the matching result. If `name` filtering returns no match (e.g. due to a
|
||||
title typo or case mismatch), fall back to listing without the filter and matching manually.
|
||||
|
||||
## Create a milestone
|
||||
|
||||
```text
|
||||
milestone_write method: "create"
|
||||
owner: <owner> repo: <repo>
|
||||
title: "v1.0"
|
||||
description: "First stable release"
|
||||
due_on: "2025-03-01T00:00:00Z"
|
||||
```
|
||||
|
||||
Report the returned ID — the caller (`gitea-issues`/`gitea-prs`) needs it to assign issues/PRs to
|
||||
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"
|
||||
```
|
||||
|
||||
Only pass the fields being changed — `id` plus any of `title`/`description`/`due_on`/`state`.
|
||||
|
||||
## Delete a milestone
|
||||
|
||||
```text
|
||||
milestone_write method: "delete" owner: <owner> repo: <repo> id: <id>
|
||||
```
|
||||
|
||||
Deleting a milestone does not delete or unassign the issues/PRs that referenced it — they simply
|
||||
lose the milestone reference.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Sources
|
||||
|
||||
## 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
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/labels.md, references/milestones.md, references/label-inference.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/issue/slim.go, https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/pull/slim.go, 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 issues, PRs, branches, commits, tags, releases, and files — including the label name-vs-ID and milestone object-vs-string representation quirks this skill's Gotchas document
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md, references/labels.md, references/milestones.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **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/labels.md, references/label-inference.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner command patterns for labels and milestones
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
name: gitea-prs
|
||||
|
||||
description: >
|
||||
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. 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
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
version: "0.1.4"
|
||||
|
||||
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.** `#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.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
Extract from the git remote before any tool call, skipping this when an orchestrating caller already passed them in:
|
||||
|
||||
```bash
|
||||
rtk git remote get-url origin
|
||||
```
|
||||
|
||||
No origin, or 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; 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` |
|
||||
|
||||
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.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
topic: merging
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
---
|
||||
|
||||
# Merge workflow
|
||||
|
||||
## Two independent gates
|
||||
|
||||
Before merging, two things can each independently block the merge, and only one of them is visible from `get_status`:
|
||||
|
||||
1. **CI status** — `pull_request_read method: "get_status"` returns the combined commit status for the PR's head commit. This reflects CI/build checks only.
|
||||
2. **Review and branch-protection state** — required approval counts, an allowlist of required approvers/teams, whether requested reviewers have reviewed, and stale-approval handling (an approval given before new commits were pushed may be auto-dismissed or ignored, depending on repo settings). None of this is exposed via `get_status` — it is enforced server-side by the merge call itself, and repository admins are not exempt from it by default (an explicit "administrators must follow branch protection" setting is what removes that bypass).
|
||||
|
||||
A green `get_status` does not mean the merge will succeed. Treat both gates as independently checkable, and expect `pull_request_write method: "merge"` to return an error if branch-protection requirements aren't met even when CI is passing.
|
||||
|
||||
## `pull_request_write method: "merge"`
|
||||
|
||||
**Parameters** (in addition to `owner`, `repo`, `pull_number` — see `references/pull-requests.md` for the full parameter list):
|
||||
- `merge_style` (string, optional, default `"merge"`) — `"merge"` | `"rebase"` | `"rebase-merge"` | `"squash"` | `"fast-forward-only"`
|
||||
- `title` (string, optional) — override the merge/squash commit's title
|
||||
- `message` (string, optional) — merge commit message (for squash, this becomes the squash commit's message — see the auto-close gotcha below)
|
||||
- `delete_branch` (boolean, optional) — delete the head branch after a successful merge
|
||||
- `force_merge` (boolean, optional) — merge even if checks are failing; use only when the user explicitly asks to override a failing CI status
|
||||
- `merge_when_checks_succeed` (boolean, optional) — queue the merge to happen automatically once checks pass, instead of merging immediately
|
||||
- `head_commit_id` (string, optional) — expected head SHA; supplying it lets the server detect if the branch moved since you last read it (conflict detection) rather than merging a stale diff
|
||||
|
||||
## Recommended sequence
|
||||
|
||||
1. Call `pull_request_read method: "get_status"` first. Report CI status to the user. Do not block the merge attempt solely because CI is failing unless the user asks you to stop — but do surface it before proceeding.
|
||||
2. Call `pull_request_write method: "merge"` with the chosen `merge_style` (ask the user if unspecified; do not default to squash silently, since squash has the closing-keyword implication below) and `delete_branch: true` unless told otherwise.
|
||||
3. If branch-protection requirements block the merge, the call itself will return the error — report it verbatim rather than retrying with `force_merge` unless the user explicitly asks to override.
|
||||
|
||||
## Merging does not auto-close linked issues
|
||||
|
||||
Gitea has no GitHub-style "merge triggers close" event. It does parse closing keywords (`Fixes #N`, `Closes #N`) in commit messages that land on the default branch — so a regular (non-squash) merge, which preserves each original commit message, can auto-close an issue this way if one of those commits used a closing keyword. A squash merge rewrites history into a single commit, so whether the keyword survives depends entirely on what `message` (or the default squash message) ends up being.
|
||||
|
||||
After any merge, call `issue_read method: "get"` (in `gitea-issues`) on any issue the PR references before deciding whether to close it explicitly. Closing an already-closed issue is a harmless no-op, but don't assume a manual close is always needed, and don't assume it never is.
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
topic: pull-requests
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# 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. 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`
|
||||
|
||||
**Parameters:**
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `state` (string, optional, default `"all"`) — `"open"` | `"closed"` | `"all"`
|
||||
- `sort` (string, optional, default `"recentupdate"`) — `"oldest"` | `"recentupdate"` | `"leastupdate"` | `"mostcomment"` | `"leastcomment"` | `"priority"`
|
||||
- `milestone` (number, optional) — milestone ID filter, not a title
|
||||
- `page` (number, optional, default 1)
|
||||
- `per_page` (number, optional, default 30)
|
||||
|
||||
List responses trim PRs down to summary fields — `head`/`base` are bare ref strings and `milestone` does not appear as an object. Don't rely on this call to resolve a milestone's ID; see the Gotcha below.
|
||||
|
||||
## `pull_request_read`
|
||||
|
||||
**Parameters:**
|
||||
- `method` (string, required) — `"get"` | `"get_diff"` | `"get_files"` | `"get_status"` | `"get_reviews"` | `"get_review"` | `"get_review_comments"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `pull_number` (number, required)
|
||||
- `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}`); 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`).
|
||||
|
||||
**Milestone gotcha:** because `pull_request_read` only ever returns a milestone title, never an ID, resolving "which milestone ID does this PR belong to" requires calling into `gitea-labels-milestones`' `milestone_read method: "list"` and matching by title. Do not attempt to infer or guess the ID. This is the read-side milestone problem; for the separate write-side one, see the `"create"` drop Gotcha under `pull_request_write`.
|
||||
|
||||
## `pull_request_write`
|
||||
|
||||
**Parameters:**
|
||||
- `method` (string, required) — `"create"` | `"update"` | `"close"` | `"reopen"` | `"merge"` | `"update_branch"` | `"add_reviewers"` | `"remove_reviewers"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `pull_number` (number, required for every method except `"create"` — this is enforced by convention, not by the tool's schema, so passing it incorrectly produces a runtime error rather than a client-side validation error)
|
||||
- `title` (string, required for `"create"`; optional for `"update"`)
|
||||
- `body` (string, required for `"create"`; optional for `"update"`)
|
||||
- `head` (string, required for `"create"`) — source branch; same-repo PRs use a bare branch name, cross-repo fork PRs use `"fork-owner:branch-name"` (see Gotcha)
|
||||
- `base` (string, required for `"create"`; optional for `"update"`) — target branch; passing it on `"update"` retargets an open PR onto a different base
|
||||
- `assignee` (string, optional) — single login; **`"update"` only** — silently dropped on `"create"` (see Gotcha)
|
||||
- `assignees` (array of strings, optional) — login names; **`"update"` only** — silently dropped on `"create"` (see Gotcha)
|
||||
- `milestone` (number, optional) — milestone ID, never a title; **`"update"` only** — silently dropped on `"create"` (see Gotcha)
|
||||
- `state` (string, optional, for `"update"`) — `"open"` | `"closed"` (no `"all"` — unlike issue state filters)
|
||||
- `allow_maintainer_edit` (boolean, optional, for `"update"`)
|
||||
- `labels` (array of numbers, optional) — label IDs, never names — resolve via `gitea-labels-milestones` first
|
||||
- `deadline` (string, optional) — ISO 8601
|
||||
- `remove_deadline` (boolean, optional) — **`"update"` only** — silently dropped on `"create"` (harmless there, but it does not do anything)
|
||||
- `reviewers` (array of strings, optional) — login names; **`"add_reviewers"`/`"remove_reviewers"` only** — silently dropped on both `"create"` and `"update"` (see Gotcha)
|
||||
- `team_reviewers` (array of strings, optional) — same as `reviewers`: `"add_reviewers"`/`"remove_reviewers"` only
|
||||
- `draft` (boolean, optional) — prepends `"WIP:"` to the title; honoured on both `"create"` and `"update"` (see Gotcha)
|
||||
|
||||
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. 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:
|
||||
|
||||
- **`labels` does apply on `"create"`.** It sits next to `milestone` in the parameter list and reads identically, so a caller who confirms the labels landed will reasonably assume the milestone did too.
|
||||
- **`issue_write method: "create"` does honour `assignees` and `milestone`.** The asymmetry is specific to pull requests; experience with the issue write does not transfer.
|
||||
|
||||
To verify a milestone actually applied, re-read the PR and check for the `milestone` key, or check that the milestone's `open_issues` count moved — the create response alone cannot tell you.
|
||||
|
||||
**Cross-repo head format:** `head` must be `"fork-owner:branch-name"` for a PR originating from a fork of the base repo. Passing a bare branch name causes Gitea to search for that branch in the base repo instead, and returns 422 when it isn't found there.
|
||||
|
||||
**Draft/WIP behavior:** `draft: true` on `"create"` is implemented by prepending `"WIP:"` to the title — there is no first-class draft boolean stored separately from the title. The title returned by subsequent reads will include the prefix. To un-draft, call `"update"` with `draft: false` and no `title` — the server reads the PR's current title and strips the prefix itself, so you do not need to reconstruct the un-prefixed text. Two prefixes are recognised for stripping — `WIP:` and `[WIP]` — matched case-insensitively, even though only `"WIP: "` is ever added. Passing `title` alongside `draft` applies the prefix rule to the title you passed rather than the stored one.
|
||||
|
||||
**`update_branch`:** takes only `owner`, `repo`, `pull_number` — no other parameters. It merges the current base branch into the PR's head branch server-side, resolving a PR that Gitea reports as behind its base (analogous to GitHub's "Update branch" button). Use it when a PR shows `mergeable: false` or an out-of-date status due to base-branch drift rather than an actual conflict; if the head and base have truly diverged with conflicting changes, this call fails and the conflict must be resolved by pushing a merge/rebase to the head branch directly, outside this skill's scope.
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
topic: reviews
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# 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`. Last verified against **v1.7.0**, as reported by `get_gitea_mcp_server_version`.
|
||||
|
||||
## Review state machine
|
||||
|
||||
A review is not a single write. It moves through states:
|
||||
|
||||
1. **`create`** — opens a review in `"PENDING"` state, optionally attaching inline comments. Nothing is visible to other users yet.
|
||||
2. **`submit`** — finalizes the pending review with a terminal `state`: `"APPROVED"`, `"REQUEST_CHANGES"`, or `"COMMENT"`. This is the point at which the review becomes visible and counts toward merge-gate requirements (see `references/merging.md`).
|
||||
3. **`dismiss`** — invalidates an already-submitted review (e.g. an approval that's no longer valid after force-push), with an optional `message` giving the reason. Dismissal does not delete the review record — it stays visible but marked dismissed.
|
||||
4. **`delete`** — removes a review outright. Use only for a review that was never submitted (e.g. abandoning a `PENDING` draft); do not use `delete` to retract a submitted review — use `dismiss` instead.
|
||||
|
||||
## `pull_request_review_write`
|
||||
|
||||
**Parameters:**
|
||||
- `method` (string, required) — `"create"` | `"submit"` | `"delete"` | `"dismiss"` | `"reply_comment"` | `"resolve_thread"` | `"unresolve_thread"`
|
||||
- `owner` (string, required)
|
||||
- `repo` (string, required)
|
||||
- `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) — 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` — 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_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.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Sources
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **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`
|
||||
|
||||
## 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 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`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **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:** references/merging.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner command patterns for PR review workflows.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** references/merging.md
|
||||
- **Status:** `extracted`
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
name: gitea-releases
|
||||
|
||||
description: >
|
||||
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: integration
|
||||
version: "0.1.2"
|
||||
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 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.
|
||||
|
||||
## Step 1 — Resolve owner and repo
|
||||
|
||||
`owner` and `repo` are required on every tool below. Extract from the git remote, unless an orchestrating caller already passed them in:
|
||||
|
||||
```bash
|
||||
rtk git remote get-url origin
|
||||
```
|
||||
|
||||
No origin, or 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 |
|
||||
|---|---|---|---|
|
||||
| List releases | `list_releases` | `owner`, `repo` | `is_draft`, `is_pre_release`, `page` (default 1), `per_page` (default 20) |
|
||||
| Get one release | `get_release` | `owner`, `repo`, `id` (number) | — |
|
||||
| Get latest release | `get_latest_release` | `owner`, `repo` | — |
|
||||
| Create release | `create_release` | `owner`, `repo`, `tag_name`, `target`, `title` | `body`, `is_draft`, `is_pre_release` |
|
||||
| Delete release | `delete_release` | `owner`, `repo`, `id` (number) | — |
|
||||
| List tags | `list_tags` | `owner`, `repo` | `page` (default 1), `per_page` (default 20) |
|
||||
| Get one tag | `get_tag` | `owner`, `repo`, `tag_name` | — |
|
||||
| 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.
|
||||
|
||||
Pass a caller-supplied `tag_name` through verbatim — the API accepts any string; semver is convention, not constraint.
|
||||
|
||||
## Step 3 — Procedure for the scenario in hand
|
||||
|
||||
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 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`.
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
topic: call-signatures
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
---
|
||||
|
||||
# Release and tag call signatures
|
||||
|
||||
Signatures and response shapes are derived from gitea-mcp source (`operation/*.go` and `slim.go`,
|
||||
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 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.
|
||||
|
||||
## Releases
|
||||
|
||||
**`list_releases`**
|
||||
- Optional: `is_draft` (boolean), `is_pre_release` (boolean), `page` (number, default 1), `per_page` (number, default 20)
|
||||
- Returns an array of release objects (shape below), one page at a time.
|
||||
|
||||
**`get_release`**
|
||||
- Required: `id` (number) — the release's numeric id, not its tag name.
|
||||
- Returns a single release object.
|
||||
|
||||
**`get_latest_release`**
|
||||
- No parameters beyond `owner`/`repo`.
|
||||
- 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)
|
||||
- Optional: `body` (string — release notes), `is_draft` (boolean), `is_pre_release` (boolean)
|
||||
- Assumed (not confirmed by the research docs) that if `tag_name` doesn't already exist as a tag, Gitea creates it against `target` as part of this call. Verify with `get_tag`/`list_tags` afterward if the caller needs certainty.
|
||||
|
||||
**`delete_release`**
|
||||
- Required: `id` (number) — same numeric id as `get_release`. Does not accept `tag_name`.
|
||||
- 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.
|
||||
|
||||
## Tags
|
||||
|
||||
**`list_tags`**
|
||||
- Optional: `page` (number, default 1), `per_page` (number, default 20)
|
||||
- Returns an array of `{ name, commit_sha }` — no `message` field on list responses.
|
||||
|
||||
**`get_tag`**
|
||||
- Required: `tag_name` (string)
|
||||
- Returns `{ name, message, commit_sha }` — the only tag call that returns `message`.
|
||||
|
||||
**`create_tag`**
|
||||
- Required: `tag_name` (string)
|
||||
- Optional: `target` (string — commitish to tag; if omitted, Gitea tags the default branch tip), `message` (string — annotated tag message)
|
||||
|
||||
**`delete_tag`**
|
||||
- Required: `tag_name` (string). Does not accept a numeric id.
|
||||
- Assumed by symmetry with `delete_release` (documented above as not deleting the underlying tag) to also not delete any release wrapping the tag — but this reverse direction is not independently confirmed by the research docs, and is the more dangerous direction to get wrong: an agent might skip an explicit `delete_release` call assuming the release survives. Verify with `list_releases`/`get_release` after calling `delete_tag` rather than assume.
|
||||
|
||||
## Pagination
|
||||
|
||||
Nothing auto-paginates. Loop `page: 1, 2, ...` until a page returns fewer items than `per_page`.
|
||||
`list_releases`/`list_tags` default `per_page` to 20, not the usual 30 — assuming 30 under-counts
|
||||
pages needed.
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
topic: conventions
|
||||
source_keys:
|
||||
- context7-websites-gitea
|
||||
- context7-gitea-tea-cli
|
||||
---
|
||||
|
||||
# Release and tag conventions
|
||||
|
||||
Practitioner conventions that inform *how* to use the mechanics in `call-signatures.md` — not
|
||||
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 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
|
||||
|
||||
Per the `tea` CLI (the reference Gitea client), tag names conventionally follow semver with a `v`
|
||||
prefix: `v1.2.0`, `v2.0.0-beta.1`. This is a convention observed by tooling and humans, not a
|
||||
Gitea-enforced constraint — the API accepts any string as `tag_name`. Don't validate or rewrite a
|
||||
caller-supplied tag name against semver; just pass it through.
|
||||
|
||||
## Draft and prerelease are explicit flags
|
||||
|
||||
`is_draft` and `is_pre_release` are booleans the caller sets directly on `create_release` — Gitea
|
||||
does not infer either from the tag name, even though the `-beta`/`-rc` suffix convention above is
|
||||
commonly used to signal a prerelease to humans. When a user asks to "cut a beta" or "publish a
|
||||
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.
|
||||
|
||||
## Release notes sourcing
|
||||
|
||||
Practitioner convention (per `tea`) is to source release notes (`body`) from a changelog file
|
||||
rather than typing them inline for each release — useful context when a caller asks to "generate"
|
||||
or "use the changelog for" release notes rather than write them from scratch.
|
||||
@@ -1,47 +0,0 @@
|
||||
# Sources
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
- **URL:** https://gitea.com/gitea/gitea-mcp
|
||||
- **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:**
|
||||
- SKILL.md (Dispatch table, Gotchas)
|
||||
- references/call-signatures.md
|
||||
|
||||
**Status:** `extracted`
|
||||
|
||||
## 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 tags and releases.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/api-reference.md (Releases and Tags response shapes)
|
||||
|
||||
**Contributing files:**
|
||||
- references/call-signatures.md (release/tag object shapes)
|
||||
|
||||
**Status:** `extracted`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **URL:** context7:/websites/gitea
|
||||
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — release and tag semantics, draft/prerelease behavior.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/workflow-conventions.md (Release and tag conventions section)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — draft/prerelease as explicit flags)
|
||||
- references/conventions.md
|
||||
|
||||
**Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner release/tag command patterns, semver tag conventions, draft/prerelease flags, release-notes-from-file conventions.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/workflow-conventions.md (Release and tag conventions section)
|
||||
|
||||
**Contributing files:**
|
||||
- references/conventions.md (semver tag naming, release-notes sourcing)
|
||||
|
||||
**Status:** `extracted`
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
name: gitea-workflow
|
||||
|
||||
description: >
|
||||
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.4"
|
||||
source_keys:
|
||||
- gitea-mcp-repo
|
||||
- gitea-mcp-slim-go
|
||||
- context7-websites-gitea
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **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.
|
||||
|
||||
## Dispatch
|
||||
|
||||
The invocation's shape selects exactly one branch.
|
||||
|
||||
| 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` |
|
||||
|
||||
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.
|
||||
|
||||
Read only the reference file matching the selected branch — each is self-contained for its concern.
|
||||
|
||||
## Report
|
||||
|
||||
Every branch ends here. Present results in plain language; the branch's reference file carries its format.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
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. A 404 here isn't proof the number doesn't exist — Gitea hides permission errors as not-found (see `gitea-issues` Gotchas). Report the 404 and suggest checking the token's `write:issue` scope 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.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Sources
|
||||
|
||||
## 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`), 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:** references/status-checkin.md, references/number-resolution.md, references/skill-index.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## gitea-mcp-slim-go
|
||||
|
||||
- **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:** references/number-resolution.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-websites-gitea
|
||||
|
||||
- **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:** references/number-resolution.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-gitea-tea-cli
|
||||
|
||||
- **URL:** context7:/git_gitea_com/gitea_tea
|
||||
- **Description:** Official `tea` CLI docs on Context7 — practitioner conventions for issues, PRs, and releases. Consulted as part of the shared research pass but its content is domain-specific (PR/release workflow patterns), out of scope for pure status/routing behavior — no content from it was used in this skill.
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
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