12 Commits

Author SHA1 Message Date
14369ae103 fix(gitea): fill provenance gap in gitea-branches sources.md
The gitea-branches skill was authored before the context7 research
backfill (workflow-conventions.md) landed on this branch, so its
sources.md was missing context7-websites-gitea and
context7-gitea-tea-cli — both present with status `extracted` in the
upstream research doc, which validate-provenance.sh requires every
consuming skill to account for. Adds context7-websites-gitea (credited
for the protected-branch gotcha) and context7-gitea-tea-cli (marked
`(none)` — its release/tag content is out of scope for branches/commits).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 10:42:24 +00:00
696079cf3e feat(gitea): add gitea-releases skill 2026-07-05 10:40:17 +00:00
a17f65db22 feat(gitea): add gitea-releases skill
Covers releases and tags (list/get/create/delete) — all verified
working with the current write:issue/write:repository token scope.
2026-07-05 10:40:05 +00:00
7d30c454ca feat(gitea): add gitea-files skill 2026-07-05 10:39:00 +00:00
0c7dd04a46 feat(gitea): add gitea-files skill
Covers get_file_contents, get_dir_contents, get_repository_tree,
create_or_update_file, delete_file — all verified working with the
current write:issue/write:repository token scope.
2026-07-05 10:38:52 +00:00
3de8ff5d15 feat(gitea): add gitea-labels-milestones skill 2026-07-05 10:38:22 +00:00
fd837b87d9 feat(gitea): add gitea-branches skill (branches + commits) 2026-07-05 10:38:22 +00:00
a3853f78b1 feat(gitea): add gitea-labels-milestones skill
Cross-cutting skill for label and milestone operations, composed by
gitea-issues and gitea-prs. Includes the label inference guide deferred
from issue #6 comment #848.
2026-07-05 10:38:09 +00:00
5854961c1f feat(gitea): add gitea-branches skill (branches + commits)
Adds plugins/gitea/skills/gitea-branches/ per ADR 0011, covering
list_branches/create_branch/delete_branch (migrated from the flat
plugins/bin/skills/gitea/ dispatch) plus list_commits/get_commit (new
read-only commit-history domain). Call signatures were re-verified live
via ToolSearch against the deployed gitea-mcp server rather than copied
from api-reference.md, per issue #6 comment #849's root-cause fix.

Bumps the gitea plugin to 1.1.0 in both manifests for the new skill.
2026-07-05 10:35:12 +00:00
4e98df7445 docs(gitea): backfill external workflow-convention research via context7
Merge research backfill for issue #6: adds external/best-practice
content (label taxonomy, PR review conventions, milestone semantics,
issue/PR cross-linking, release semver conventions) sourced from
context7, closing the gap left by the original docs.gitea.com timeout.
2026-07-05 10:23:35 +00:00
0d2a6cd839 docs(adr): record gitea plugin deep-module redesign decision
Captures the grill-with-docs session for issue #6: relocate gitea
skill from plugins/bin/ to plugins/gitea/, split into 6 domain skills
plus a workflow orchestrator and orchestrate agent, expand scope to
3 token-verified new domains, and resolve the schema-verification
question from comment #849.
2026-07-05 10:23:34 +00:00
12f60f42b7 docs(gitea): backfill external workflow-convention research via context7
Existing gitea research docs were 100% code-derived from gitea-mcp
source with zero external content (the original docs.gitea.com fetch
timed out and was never retried). Adds workflow-conventions.md sourced
from context7 /websites/gitea and /git_gitea_com/gitea_tea: scoped/
exclusive label conventions, PR review/branch-protection rules,
release/tag semver conventions, and automatic issue/PR cross-reference
linking (validates the dependency-linking convention for issue #6).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 10:23:11 +00:00
25 changed files with 1286 additions and 4 deletions

View File

@@ -0,0 +1,109 @@
# Gitea skill splits into deep modules under `plugins/gitea/`, replacing the flat `plugins/bin/skills/gitea/`
The gitea skill originated under kyberforge (`b9c73cc`), moved to `plugins/bin/skills/gitea/`
(`4f603cd`), and covers only 5 of gitea-mcp's ~15 tool domains (issues, labels, milestones, PRs,
branches) in one flat `SKILL.md` mixing routing logic with execution detail. Meanwhile
`plugins/gitea/` already existed as a plugin scaffold holding comprehensive research docs (all 55
MCP tool schemas, code-derived from gitea-mcp source, at
`plugins/gitea/docs/research/docs/gitea/`) but empty `skills/`, `agents/`, and `.mcp.json`. This
ADR records the decisions from a grill-with-docs session on issue #6 that splits the flat skill
into deep modules and relocates it to `plugins/gitea/`.
**Relocation.** The new deep-module skill structure is built in `plugins/gitea/`, not
`plugins/bin/`, making the gitea plugin self-contained — bundling its own skills, agents, and MCP
config — matching this repo's Plugin glossary definition (the deployable unit that bundles skills,
agents, hooks, and MCP servers into a single installable directory) and mirroring the existing
`plugins/git/` plugin's shape. The old flat skill stays at `plugins/bin/skills/gitea/` untouched
for now, kept as a reference/fallback — not deleted in this pass; removal is a future cleanup once
the new structure is validated in practice.
**Scope expansion.** Coverage expands beyond the original 5 domains to 3 new domains verified
working with the current token scope (`write:issue`, `write:repository`) per
`plugins/bin/skills/gitea/references/token-access.md`: Files (get/create/update/delete file, dir
contents, repo tree), Commits (list/get), and Releases & Tags (full CRUD). Domains not added:
repo/org listing, user identity, notifications, and packages are blocked by token scope
(`read:user`, `read:organization`, `read:notification`, `read:package`); Actions/CI (list_runs and
secrets return 403, writes untested) and Wiki (404 on this repo, writes untested) are partially
broken or unverified. All are deferred to future issues once scope is expanded or the domain is
verified safe elsewhere.
**Domain skill split.** The flat skill becomes 6 domain skills plus a workflow orchestrator and an
agent counterpart, composed per the Skill composition pattern:
- `gitea-issues` — issues only (list/read/write/search); closes out 4 enrichments deferred from
issue #6 comment #848 — milestone assignment on create, assignee on create (documented
workaround since `get_me`/`read:user` is blocked), dependency-linking convention ("Depends on
#N" in body, since gitea-mcp has no native dependency field) — and delegates label inference to
`gitea-labels-milestones`.
- `gitea-labels-milestones` — split out as its own shared skill since labels/milestones are
cross-cutting (apply to both issues and PRs), rather than bundled under `gitea-issues`; owns the
label inference guide (context-pattern → Kind/*/Priority/*/Status/* taxonomy mapping).
- `gitea-prs` — pull requests + reviews, composes `gitea-labels-milestones` for label/milestone
application.
- `gitea-branches` — branches + commits bundled together (commits are read-only history within
branches, a natural pairing).
- `gitea-files` — new domain.
- `gitea-releases` — releases + tags bundled together.
- `gitea-workflow` — thin human-facing orchestrator mirroring `git-workflow`
(`plugins/git/skills/git-workflow/`). Preserves the original flat skill's default no-args status
view (composes `gitea-issues` + `gitea-prs`) and routes ambiguous requests to the right domain
skill. Named `gitea-workflow`, not bare `gitea`, for naming consistency with the other 6 skills,
despite breaking the old `/gitea` invocation muscle memory — an explicit accepted tradeoff.
- `gitea-orchestrate` (agent, not skill) — agent-facing deterministic counterpart mirroring
`git-orchestrate`, for multi-step composition when the caller is an agent rather than a human.
**Reference-file signature sourcing.** Each new skill's `references/*.md` restates verified MCP
call signatures cross-checked live via `ToolSearch` at authoring time, not copied from
`api-reference.md`, which could drift from the deployed MCP server version. This resolves issue #6
comment #849's root-cause question about the original `type` parameter bug, which happened
because the skill was authored from Gitea REST API docs instead of the actual MCP tool schema.
This is applied manually during this authoring pass; the `kyberforge:skill-author` meta-skill
itself is not changed — comment #849's "option 2" process fix is considered and explicitly
deferred as out of scope for this PR.
**MCP config deferred.** `plugins/gitea/.mcp.json` is deliberately left as an empty `mcpServers`
block — the real gitea-mcp server config continues to live in the user's `~/.claude.json` rather
than being wired into the plugin manifest. This means the gitea plugin is not yet installable
standalone via `claude plugin install gitea@holocron` without manual MCP setup. A follow-up Gitea
issue tracks closing this gap.
**Research backfill.** The existing research docs
(`plugins/gitea/docs/research/docs/gitea/`) are 100% code-derived from gitea-mcp source with zero
external/best-practice content (the original docs.gitea.com fetch timed out and was never
retried). Context7 has `/websites/gitea` (official docs mirror) and `/git_gitea_com/gitea_tea` (Tea
CLI) available now — backfilled via a parallel research pass before skill-authoring, so the
Provenance chain (`source_keys` → `sources.md` → research doc) has real external sources for
workflow/convention guidance, not just API mechanics.
**Authoring route.** All 8 artifacts (7 skills + 1 agent) are authored via `kyberforge:forge`, not
direct `skill-author`/`agent-author` calls, even though `forge`'s own routing rule would normally
bypass itself here since the target artifact types are already known — chosen deliberately for
uniform audit/recheck coverage across every artifact.
## Considered options
**5-skill split, labels+milestones bundled under `gitea-issues` (rejected)** — simpler, one fewer
skill, but re-buries label/milestone logic inside an issues-specific skill even though PRs need it
equally, forcing `gitea-prs` to either duplicate the guide or reach into `gitea-issues`'
`references/` — breaking the self-contained skill boundary.
**8-skill split, one skill per raw API domain, no bundling (rejected)** — e.g. separate
`gitea-commits` and `gitea-tags` skills. Rejected as over-fragmentation: commits are read-only
history naturally scoped to branches, and tags are naturally scoped to releases, so bundling
avoids two near-empty skills each routing to a single tool family.
## Consequences
- `plugins/gitea/` gains `skills/gitea-issues/`, `skills/gitea-labels-milestones/`,
`skills/gitea-prs/`, `skills/gitea-branches/`, `skills/gitea-files/`, `skills/gitea-releases/`,
`skills/gitea-workflow/`, and `agents/gitea-orchestrate.md` (+ Copilot counterpart), each with
its own `references/` and provenance records.
- `plugins/gitea/.mcp.json` stays an empty `mcpServers` block until the follow-up issue wires in
the real gitea-mcp server config; the plugin is not standalone-installable until then.
- `plugins/bin/skills/gitea/` remains in place, unreferenced by new work, until a future cleanup
issue removes it once the new structure is validated in practice.
- Follow-up issues are needed for: the deferred domains (Actions/CI, Wiki, Notifications,
Packages, User/Org), the `.mcp.json` wiring gap, and the eventual removal of
`plugins/bin/skills/gitea/`.
- Future domain-plugin work in this repo can point to this ADR as the template for splitting an
MCP-wrapping skill into deep modules.

View File

@@ -10,9 +10,10 @@
"issues", "issues",
"prs", "prs",
"milestones", "milestones",
"releases" "releases",
"branches"
], ],
"license": "MIT", "license": "MIT",
"name": "gitea", "name": "gitea",
"version": "1.0.0" "version": "1.1.0"
} }

View File

@@ -20,3 +20,17 @@
- **Description:** Gitea REST API swagger documentation covering underlying endpoints for issues, PRs, labels, milestones, and branches - **Description:** Gitea REST API swagger documentation covering underlying endpoints for issues, PRs, labels, milestones, and branches
- **Contributing files:** (see notes below) - **Contributing files:** (see notes below)
- **Status:** `no content extracted` — source fetch timed out; all reference content derived from gitea-mcp source files which are authoritative for MCP tool usage - **Status:** `no content extracted` — source fetch timed out; all reference content derived from gitea-mcp source files which are authoritative for MCP tool usage
## context7-websites-gitea
- **URL:** context7:/websites/gitea
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — scoped/exclusive label conventions, branch protection and PR review/merge rules, release and webhook semantics, issue/PR automatic cross-reference linking. Backfills the external/best-practice gap left by the original docs.gitea.com fetch timeout.
- **Contributing files:** workflow-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 command patterns for issues, PRs, and releases, including semver tag/release conventions, draft/prerelease flags, and release-notes-from-file conventions.
- **Contributing files:** workflow-conventions.md
- **Status:** `extracted`

View File

@@ -0,0 +1,88 @@
---
topic: workflow-conventions
source_keys:
- context7-websites-gitea
- context7-gitea-tea-cli
---
# Gitea Workflow Conventions
Practitioner conventions and platform behavior that inform *how* to use the mechanics already
documented in `api-reference.md` — not additional tool schemas.
## Scoped, exclusive labels
Gitea labels support a scoped-label convention: a `/` delimiter in the label name (e.g.
`Priority/High`) plus an `exclusive: true` flag means only one label from that scope can be applied
to an issue or PR at a time — applying a new `Priority/*` label automatically replaces the previous
one. This is exactly the `Kind/*` / `Priority/*` / `Status/*` taxonomy already used in this repo's
own label set, confirming the taxonomy follows Gitea's native scoped-label convention rather than an
ad hoc naming scheme. When defining or inferring labels, a scope prefix implies mutual exclusivity —
label inference logic should replace, not add to, existing labels in the same scope.
Repositories can also seed a predefined label set at creation time from a YAML label file
(`name`, `color`, `description`, `exclusive`), which is where the base `Kind/Priority/Status` sets
typically originate.
## Milestone and label state as first-class transitions
Both issues and PRs treat labeling and milestoning as discrete state-transition events
(`label_updated`/`label_cleared`, `milestoned`/`demilestoned`), not passive metadata fields. This
reinforces treating `gitea-labels-milestones` as a shared skill: the same transition semantics apply
whether the target is an issue or a PR.
## Automatic cross-reference linking
Gitea auto-renders issue/PR references in body text without any API call: `#1234` and `!1234` both
resolve to issue/PR 1234 in the same repo (issues and PRs share one number space, consistent with
`data-model.md`); cross-repo references use `owner/repo#1234` (issue) or `owner/repo!1234` (PR). This
directly validates the dependency-linking convention decided for `gitea-issues` (issue #6, comment
#848): writing `Depends on #N` in an issue body is not just a text convention — Gitea renders it as
a real clickable cross-reference automatically, with no separate API call required. For
external-issue-tracker repos, the same syntax renders as an external link instead, so the skill
should assume same-repo internal linking unless told otherwise.
## Pull request review workflow
The reviewer flow is: comment, request changes, or approve; the author pushes updates to the same
branch, which the PR auto-tracks; maintainers merge once approved. Protected branches can layer
additional constraints on top of this base flow:
- An allowlist of users/teams may be required to approve before merge is possible.
- A minimum approval count can be enforced.
- Stale approvals (approvals given before new commits were pushed) can be auto-dismissed or ignored.
- Merge can be blocked if any review requests changes, if requested reviewers haven't reviewed yet,
or if the branch is outdated relative to its base.
- Repository admins are not exempt from these rules by default — an explicit
"administrators must follow branch protection rules" setting is what removes their force-merge
bypass.
A skill that merges PRs should treat "CI passing" and "reviews satisfied" as two independently
checkable gates — `get_status` covers CI, but review/approval state and branch-protection
constraints are a separate check the merge call itself will enforce server-side and return as an
error if unmet.
## Release and tag conventions
Releases are conceptually separate from tags but always tied to one: a release wraps a tag with a
title, notes, and optional binary assets. Practitioner convention (per the `tea` CLI, the reference
Gitea client) is:
- Tag names are semver-style, typically `v`-prefixed (`v1.2.0`, `v2.0.0-beta.1`).
- Release notes are commonly sourced from a changelog file rather than typed inline.
- Draft and prerelease are separate boolean flags, not states inferred from the tag name — a
prerelease is anything with a `-beta`/`-rc` style suffix by convention, but Gitea does not enforce
this; the skill should treat `draft`/`prerelease` as flags the caller sets explicitly rather than
something to infer from the tag string.
- Deleting a release does not delete its tag by default — the two are separate destructive
operations (confirmed by `delete_release` taking a numeric release ID per `troubleshooting.md`,
distinct from `delete_tag`).
## File editing: direct commit vs. PR
Gitea's own UI defaults to prompting for a target branch when creating/editing a file directly
through the web interface, and supports pre-filling a new file's path and content via query
parameters — reflecting that direct-commit file edits are a first-class, expected workflow (not just
an API escape hatch). This supports `create_or_update_file`/`delete_file` being used directly against
a working branch as a normal editing action, with the SHA-currency requirement (`troubleshooting.md`)
being the main gotcha rather than direct-commit being an anti-pattern to avoid.

View File

@@ -11,7 +11,8 @@
"issues", "issues",
"prs", "prs",
"milestones", "milestones",
"releases" "releases",
"branches"
], ],
"license": "MIT", "license": "MIT",
"mcpServers": ".mcp.json", "mcpServers": ".mcp.json",
@@ -19,5 +20,5 @@
"skills": [ "skills": [
"skills/" "skills/"
], ],
"version": "1.0.0" "version": "1.1.0"
} }

View File

@@ -0,0 +1,35 @@
# gitea-branches
Manage Gitea repository branches and inspect commit history via the Gitea MCP server.
## What it does
This skill handles branch lifecycle operations (list, create, delete) and read-only commit
history (list commits, get a single commit's full detail) against a Gitea repository. It resolves
`owner`/`repo` from the git remote, dispatches to the right MCP tool, and applies safety and
pagination conventions specific to Gitea's API (e.g. refusing to delete a protected branch without
explicit confirmation, and treating unexpected 404s as possible masked 403s).
## Before you start
Requires a Gitea MCP server configured with a token. `list_branches`, `list_commits`, and
`get_commit` work with `write:issue` alone; `create_branch` and `delete_branch` need
`write:repository`. Requires a git remote named `origin` pointing at the Gitea instance.
## Usage
```
/gitea-branches
```
Describe your task: list/create/delete a branch, or list/inspect commits. See `SKILL.md`'s
dispatch table for the full set of recognized invocations.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents — dispatch table, gotchas |
| `references/branches.md` | Verified call signatures and mechanics for list/create/delete branch |
| `references/commits.md` | Verified call signatures and mechanics for list/get commit |
| `references/sources.md` | Research sources backing the branch/commit guidance |

View File

@@ -0,0 +1,66 @@
---
name: gitea-branches
description: >
Use when managing Gitea repository branches — listing, creating, or deleting
branches — or inspecting commit history within a Gitea repo: listing commits
(optionally filtered by branch or file path) or getting full detail for a
single commit by SHA. Triggers on "list branches", "create a branch",
"delete a branch", "what commits are on this branch", "show commit <sha>",
"what changed in that commit" — even if the user doesn't say "Gitea"
explicitly, as long as the repo's remote is a Gitea instance. Do not use for
local git branch/commit operations on your working copy (use git-branches or
git-history) or for PR-side branch references like cross-repo fork PR heads
(use gitea-prs).
compatibility: Requires Gitea MCP server configured with a token; list_branches, list_commits, and get_commit work with write:issue alone, create_branch and delete_branch require write:repository. Requires git remote "origin" pointing to the Gitea instance.
metadata:
category: integration
version: "0.1.0"
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
allowed-tools: Bash mcp__gitea__list_branches mcp__gitea__create_branch mcp__gitea__delete_branch mcp__gitea__list_commits mcp__gitea__get_commit
---
## Gotchas
- **Never delete a protected branch (`main`/`master` by name, or `protected: true` from `list_branches`) without explicit confirmation.** `delete_branch` is a direct API call, not a local `git push` — there is no client-side force-push guard protecting it. Name-matching `main`/`master` is a convenient default but not authoritative — a repo can protect a differently-named default branch. When in doubt, call `list_branches` first and check `protected` on the target; treat deletion of any protected branch as a hard refusal unless the user explicitly confirms in the conversation.
- **404 may actually mean 403.** Gitea hides permission errors as not-found to avoid leaking resource existence. If any of these five tools returns 404 unexpectedly, check token scope (see `references/branches.md` / `references/commits.md`) before concluding the branch or commit doesn't exist.
- **Pagination is manual.** `list_branches` and `list_commits` return one page at a time — no auto-pagination in the MCP layer. When you need a complete list, iterate `page: 1, 2, ...` until the returned count is less than `per_page`.
- **Owner/repo always come from the git remote, never from `get_me`.** Resolve them via `git remote get-url origin` (Step 1 below). `get_me`/`list_my_repos` are blocked under the token scopes this skill assumes.
- **`create_branch`'s source is `old_branch`, not "wherever gitea-mcp feels like."** Omitting `old_branch` forks from the repo's server-side default branch — not necessarily the branch you're currently working on locally. If you want to branch from your current checkout, pass `old_branch` explicitly.
## Step 1 — Resolve owner and repo
Before any tool call, extract `owner` and `repo` from the git remote:
```bash
git remote get-url origin
```
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
## Step 2 — Dispatch
| Invocation | Action |
|---|---|
| `/gitea-branches` or `/gitea-branches list` | List branches |
| `/gitea-branches create <name> [from <base>]` | Create branch |
| `/gitea-branches delete <name>` | Delete branch |
| `/gitea-branches commits [on <branch>] [touching <path>]` | List commit history |
| `/gitea-branches commit <sha>` | Get full detail for one commit |
For branch operations (list/create/delete), read `references/branches.md`.
For commit operations (list/get), read `references/commits.md`.
## Step 3 — Report
For reads: display branches as name + protected flag; display commits as SHA (short), message summary, author, date.
For writes (create/delete): confirm the action taken, the branch name, and (for create) the base it forked from.
For errors: surface the HTTP code and message. If a 404 is unexpected, re-check token scope per the Gotchas above before reporting "not found" to the user.

View File

@@ -0,0 +1,78 @@
---
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`
at authoring time, not copied from research docs — this is deliberate: research docs are generated
from source code at a point in time and can drift from the server actually deployed. Re-verify
against the live schema if these tools appear to behave differently than documented here.
## `list_branches`
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `page` (number, optional, default: `1`)
- `per_page` (number, optional, default: `30`)
**Call:**
```
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 if you need the full list (see Gotchas in SKILL.md) — iterate `page` until the returned
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:**
```
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
(`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`.
## `delete_branch`
**Parameters:**
- `owner` (string, required)
- `repo` (string, required)
- `branch` (string, required)
**Call:**
```
delete_branch owner: <owner> repo: <repo> branch: <name>
```
Before calling this, see the hard-refusal Gotcha in SKILL.md. If the target branch's name isn't
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` works with `write:issue` alone. `create_branch` and `delete_branch` need
`write:repository`. All three are verified working empirically under a token with both scopes
(`write:issue` + `write:repository`).

View File

@@ -0,0 +1,67 @@
---
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` at authoring time,
not copied from research docs, for the same drift-avoidance reason noted in `references/branches.md`.
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:**
```
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 manual-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:**
```
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 read-only and work with `write:issue` alone (no `write:repository` needed), verified
empirically against the deployed server.

View File

@@ -0,0 +1,41 @@
# 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 at authoring time — they are not 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 (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Informed the dispatch table and pagination / 404-may-mean-403 gotchas in SKILL.md, and the list/create/delete branch and list/get commit mechanics (including 409 conflict and default-branch fallback behavior) in references/branches.md and references/commits.md.
- **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; 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`

View File

@@ -0,0 +1,23 @@
# gitea-files
Read and write individual files and directory/repository trees in a Gitea repository via the Gitea MCP server.
## What it does
This skill handles file-domain operations within the Gitea integration suite: reading a single file's contents, listing one directory level, walking a full repository tree (optionally recursive), creating or updating a file, and deleting a file. It owns the SHA-based optimistic-concurrency pattern that Gitea requires for file writes — the domain's sharpest gotcha — and defers branch creation, commit history, and pull request mechanics to `gitea-branches` and `gitea-prs`.
## Usage
```
/gitea-files
```
Describe the file task: read a file or directory, walk a tree, create/update a file, or delete a file. Provide `owner`/`repo`/branch (or ask the user if not given) — this skill does not resolve them from a git remote itself.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/examples.md` | Canonical call sequences: branch + file + PR, recovering a missing SHA before an update, deleting a file |
| `references/sources.md` | Research sources backing the SHA/concurrency and direct-commit-vs-PR guidance |

View File

@@ -0,0 +1,53 @@
---
name: gitea-files
description: >
Use when reading or writing individual files or directory trees in a Gitea repository via the
Gitea MCP server: reading a file's contents, listing a directory, walking a full repository
tree, creating a new file, updating an existing file, or deleting a file. Triggers on "read this
file from the repo", "what's in this directory", "show me the repo tree", "create/update a file
in Gitea", "commit this file to the branch", "delete this file from the repo" — even when the
user doesn't say "Gitea" explicitly, as long as the target is a Gitea-hosted repository. Do not
use for local filesystem file operations (use Read/Write/Edit), for branch or commit history
(use gitea-branches), or for opening a pull request around a file change (use gitea-prs after
the file write completes here).
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:
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
- **SHA is the concurrency token for every write — and it lives at the top level of `get_file_contents`'s response, not nested under `content`.** `create_or_update_file` without `sha` is always treated as a *create*: if the path already exists, Gitea returns HTTP 409. `delete_file` has no optional path at all — omitting `sha` returns HTTP 422. The safe sequence for any update or delete is always: call `get_file_contents` first, read the top-level `sha` field, then pass that exact value to the write call. Never guess or reuse a stale SHA — a mismatched SHA is rejected the same as a missing one.
- **`get_dir_contents` and `get_repository_tree` are not SHA sources for a specific file's write.** `get_dir_contents` entries carry no `sha` at all. `get_repository_tree` entries do carry a `sha` (a blob/tree hash), but fetching it means an extra round trip with no content — `get_file_contents` is the canonical path since it returns the decoded content and the write-ready `sha` in one call.
- **`owner` and `repo` are always caller-supplied inputs, never resolved here.** This skill doesn't infer them from a git remote. If invoked directly by a human, ask for them if not stated. If invoked by `gitea-workflow` or an orchestrating agent, expect them to already be resolved and passed in.
- **Direct commits to a branch are a first-class action, not a workaround.** Gitea's own web UI defaults to editing files directly against a branch — `create_or_update_file`/`delete_file` used that way is normal, not an API escape hatch to avoid. The SHA-currency requirement above is the actual risk to manage, not the act of committing directly.
- **`ref` (reads) vs. `branch_name` (writes) are different parameters for the same concept.** `get_file_contents`, `get_dir_contents`, and `get_repository_tree` (as `tree_sha`) all accept a branch name, tag, or commit SHA to select what to read. `create_or_update_file` and `delete_file` instead take `branch_name` — the branch the commit lands on. Don't conflate the two when chaining a read into a write.
- **Content is base64.** `create_or_update_file`'s `content` parameter is base64-encoded file content, not raw text — encode before calling. `get_file_contents`'s response content is likewise base64-encoded (decode after reading), unless `withLines: true` is passed for a numbered-line view.
## Reading
- **Single file:** `get_file_contents(owner, repo, ref, path)`. Pass `withLines: true` only when you need line numbers for referencing specific lines (e.g. quoting a snippet back to the user); omit it for a normal content fetch.
- **One directory level:** `get_dir_contents(owner, repo, ref, path)` — returns immediate entries only (name, path, type, size), no recursion, no SHA, no content.
- **Whole tree:** `get_repository_tree(owner, repo, tree_sha, recursive)` — `tree_sha` accepts a SHA, branch, or tag name despite the name. Set `recursive: true` to walk subdirectories in one call. Response includes `truncated: true` when a page doesn't hold every entry — page through with `page`/`per_page` (default `page: 1`, `per_page: 30`) until you get fewer results than `per_page`.
## Writing
- **Creating a new file:** call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted entirely.
- **Updating an existing file:** call `get_file_contents(owner, repo, ref: branch_name, path)` first, take the top-level `sha`, then call `create_or_update_file(..., sha: <that value>)`.
- **Deleting a file:** call `get_file_contents` first the same way, then `delete_file(owner, repo, path, message, branch_name, sha: <that value>)` — `sha` is required, no create-style fallback exists.
- **Creating a new branch as part of the write:** pass `new_branch_name` on `create_or_update_file` to branch off before the commit lands, instead of calling a separate branch-creation step.
If the change needs review before merging, or targets a protected branch, hand off to `gitea-prs` after the write lands here to open the pull request — this skill's scope ends at the commit.
If you need the full multi-call sequence rather than the single-call summary above — e.g. branching off as part of a file push ahead of opening a PR, or recovering a SHA you didn't capture earlier — read `references/examples.md`.

View File

@@ -0,0 +1,65 @@
---
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Canonical call sequences
## Push a file to a new branch, then open a PR
```
1. get_repository_tree or get_file_contents on the base branch — only needed if the
new file is actually replacing an existing one; skip for a brand-new path.
2. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<base64-encoded content>"
message: "docs: add example"
branch_name: "main"
new_branch_name: "feat/add-example" ← branches off before the commit lands
(sha omitted — this is a new file)
3. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
```
`new_branch_name` on `create_or_update_file` replaces a separate branch-creation call — the branch is created and the commit lands on it in one step.
## Update a file when you don't already have its SHA
SHA is mandatory for updates. If it wasn't captured earlier in the conversation:
```
1. get_file_contents
owner, repo
ref: "main"
path: "docs/example.md"
→ read the top-level `sha` field (not content.sha)
2. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<new base64-encoded content>"
message: "docs: update example"
branch_name: "main"
sha: "<sha from step 1>"
```
Do not guess or omit the SHA — the write either fails (409 on create-path fallback) or is rejected outright.
## Delete a file
Same SHA-first pattern, no fallback path:
```
1. get_file_contents owner, repo, ref: "main", path: "docs/old-example.md"
→ read the top-level `sha` field
2. delete_file
owner, repo
path: "docs/old-example.md"
message: "docs: remove old example"
branch_name: "main"
sha: "<sha from step 1>"
```

View File

@@ -0,0 +1,36 @@
# 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.
**Source:** https://gitea.com/gitea/gitea-mcp
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
**Contributing files:**
- SKILL.md (Gotchas, Reading, Writing — tool parameters and SHA/concurrency behavior, cross-checked live against the deployed MCP tool schemas via ToolSearch)
- references/examples.md (canonical call sequences)
## 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 — top-level `sha` field location, `get_dir_contents`/`get_repository_tree` not being usable SHA sources for a file write)
- references/examples.md (SHA-first update/delete sequences)
## 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 (Gotchas — "Direct commits to a branch are a first-class action, not a workaround")

View File

@@ -0,0 +1,25 @@
# gitea-labels-milestones
Read and write Gitea labels and milestones, and resolve label/milestone identity for the skills that apply them to issues and PRs.
## What it does
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required for any write, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy. It is a cross-cutting shared skill composed by `gitea-issues` and `gitea-prs`, which call into it for label/milestone resolution before their own `issue_write`/`pull_request_write` calls apply the resolved IDs.
## Usage
```
/gitea-labels-milestones
```
Describe the label or milestone task: list labels, resolve a name to an ID, create/edit/delete a label, or list/create/update/close/delete a milestone. For applying already-resolved labels or a milestone to a specific issue or PR, use `gitea-issues` or `gitea-prs` instead.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents — dispatch table and Gotchas |
| `references/labels.md` | Execution detail for `label_read`/`label_write` |
| `references/milestones.md` | Execution detail for `milestone_read`/`milestone_write` |
| `references/label-inference.md` | Context-pattern → `Kind/*`/`Priority/*`/`Status/*` label inference guide |
| `references/sources.md` | Research sources backing the label/milestone guidance |

View File

@@ -0,0 +1,58 @@
---
name: gitea-labels-milestones
description: >
Use when reading or writing Gitea labels or milestones — listing repo/org labels, creating,
editing, or deleting a label, resolving label names to the numeric IDs required for applying
them to an issue or PR, or listing, creating, updating, closing, or deleting a milestone. This is
a cross-cutting shared skill: `gitea-issues` and `gitea-prs` both compose it whenever they need to
apply labels or assign a milestone, rather than duplicating label/milestone logic. Also use for
label inference — mapping a bug report, feature request, or urgency signal in conversation
context to the repo's `Kind/*`/`Priority/*`/`Status/*` label taxonomy. Do not use for applying
already-resolved label IDs or milestone IDs to a specific issue or PR — that write goes through
`issue_write`/`pull_request_write` in `gitea-issues`/`gitea-prs`, not here.
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.0"
allowed-tools: mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write
---
## Gotchas
- **Label writes take IDs, reads return names.** `label_read` is the only tool that returns full label objects (`id`, `name`, `color`, `description`). Issue/PR responses slim labels down to name strings. Before any label is applied to an issue or PR (in `gitea-issues`/`gitea-prs`), resolve names → IDs here via `label_read method: "list_repo_labels"` — never pass a name string where an ID is expected.
- **Milestones are referenced by ID everywhere, never by title.** `milestone_write` update/delete take `id`. The one place titles show up as the sole handle is the `pull_request_read` response (see next gotcha).
- **Milestone representation differs between issues and PRs.** `issue_read` returns `milestone: {id, title}` — an object. `pull_request_read` returns `milestone: "title string"` — title only, no ID. You cannot recover a milestone ID from a PR response directly; call `milestone_read method: "list"` and match by title instead.
- **Repo labels and org labels are separate pools, never mixed in one call.** `label_read`/`label_write` take either `owner`+`repo` (repo-scoped methods) or `org` (org-scoped methods) — passing both or neither for a given method is a caller error, not something the schema enforces for you. Repo and org labels can both apply to the same issue, but you list/create/edit them through different method values.
- **`milestone_write` accepts `"update"` and `"edit"` as the same operation.** Both method values map to the identical update call. Prefer `"update"` for consistency with `issue_write`/`pull_request_write`.
- **A `/` in a label name plus `exclusive: true` means mutual exclusivity, not just a naming convention.** This repo's `Kind/*`, `Priority/*`, `Status/*` labels follow Gitea's native scoped-label feature: applying a new label within a scope (e.g. `Priority/High`) is expected to replace any existing label in that same scope, not add alongside it. Label inference (see `references/label-inference.md`) must respect this — replace, don't stack.
- **Pagination is manual on every list call.** `label_read` and `milestone_read` both default to `per_page: 30`. Iterate `page: 1, 2, ...` until the result count is less than `per_page` — there is no cursor or auto-pagination.
- **Schema requiredness differs between the two tool families.** `milestone_read`/`milestone_write` have `owner` and `repo` as hard-required parameters (the call fails validation without them). `label_read`/`label_write` only hard-require `method` — `owner`/`repo`/`org` are functionally required per method but not schema-enforced, so passing none produces a runtime error from Gitea, not a client-side validation error.
## Dispatch
| Task | Tool | method |
|---|---|---|
| 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"` |
For full parameter detail and step-by-step call sequences, read `references/labels.md` (label operations) or `references/milestones.md` (milestone operations). For mapping conversation context to a label to apply, read `references/label-inference.md`.
Applying resolved label IDs or a milestone ID to a specific issue or PR is out of scope here — that's `issue_write`/`pull_request_write` in the composing skill (`gitea-issues`/`gitea-prs`).

View File

@@ -0,0 +1,64 @@
---
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.
## Scoped labels are mutually exclusive — replace, don't stack
Each of `Kind/*`, `Priority/*`, `Status/*` is a Gitea scoped-label group (the `/` delimiter plus
`exclusive: true` on the label). Applying a new label within a scope is expected to replace any
existing label in that same scope on the target issue/PR, not add alongside it. When inference
selects a `Priority/High` label and the issue already carries `Priority/Medium`, the write should
result in only `Priority/High` remaining — use `replace_labels` scoped to that group's labels, or at
minimum remove the superseded label before adding the new one. Never leave two labels from the same
scope applied at once.
## 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` |
**`Priority/*`** (urgency):
| Signal in context | Label |
|---|---|
| "blocking", "critical", "urgent", production-down | `Priority/Critical` |
| "soon", "high priority", "should do this sprint" | `Priority/High` |
| 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` |
## Procedure
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the
signals above.
2. Call `label_read method: "list_repo_labels"` (see `references/labels.md`) to get the current
label set with IDs — inference must never guess an ID, only a name, then resolve it.
3. Match inferred label names against the resolved list (case-insensitive). If a scope group
already has a different label applied on the target and a new one is inferred for that same
scope, plan to replace rather than add (see above).
4. **Low-confidence inference omits the label.** If no signal confidently maps to a `Kind/*` value,
do not guess — omit `Kind/*` entirely rather than default to one. `Priority/Medium` is the one
exception: it's the explicit default when no urgency signal is present, not a guess.
5. Hand the resolved IDs (plus which scopes to replace) to the caller's `issue_write`/
`pull_request_write` call — this skill does not apply labels to an issue or PR itself.

View File

@@ -0,0 +1,97 @@
---
topic: labels
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# 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 | org labels only |
| `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
```
label_read method: "list_repo_labels" owner: <owner> repo: <repo> per_page: 50
```
Paginate (`page: 1, 2, ...`) until the returned count is less than `per_page`. This is the only way
to build a complete name → ID map — there is no lookup-by-name endpoint.
## Get one label
```
label_read method: "get_repo_label" owner: <owner> repo: <repo> id: <id>
```
## Resolve a name to an ID
There is no direct name lookup. List all repo labels (paginating if needed), scan for a
case-insensitive name match, and extract `id`. This is the required first step before any label
application on an issue or PR — the actual `add_labels`/`replace_labels`/`remove_label` call lives
in `gitea-issues`/`gitea-prs` via `issue_write`/`pull_request_write`, which take numeric IDs only.
## Create a label
```
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.
## Edit a label
```
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
```
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.

View File

@@ -0,0 +1,98 @@
---
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
```
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
```
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:
```
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
```
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
```
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
```
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.

View File

@@ -0,0 +1,33 @@
# 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/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`

View File

@@ -0,0 +1,24 @@
# gitea-releases
Manage Gitea releases and tags — list, create, and delete releases (with draft/prerelease flags and notes) and their underlying tags.
## What it does
This skill handles release and tag operations for a Gitea repository. It creates releases from a tag/target commitish with title, notes, and draft/prerelease flags; lists and paginates releases and tags; retrieves the latest release; and deletes releases and tags as separate, independent destructive operations. It resolves the numeric release id required for deletion instead of assuming a tag name will work.
## Usage
```
/gitea-releases
```
Describe your release/tag task: list releases, get the latest release, create a release (with a tag, target, and title), or delete a release or tag. The skill handles resolving the numeric release id where required and keeps release/tag deletion as distinct operations.
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `references/call-signatures.md` | Verified tool parameters and response shapes for all 9 release/tag tools |
| `references/conventions.md` | Semver/draft/prerelease practitioner conventions and pagination behavior |
| `references/sources.md` | Research sources backing the call signatures and conventions |

View File

@@ -0,0 +1,52 @@
---
name: gitea-releases
description: >
Use when managing Gitea releases and tags for a repository: listing, creating, or deleting
releases (with draft/prerelease flags and release notes), and listing, creating, or deleting the
underlying git tags. Use even if the user doesn't say "release" explicitly — "cut a v1.2.0",
"publish a prerelease", "tag this commit", or "what's the latest release" all apply. Do not use
for git branch or commit history operations (use gitea-branches) or for issue/PR management (use
gitea-issues / gitea-prs).
metadata:
category: gitea
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
- context7-gitea-tea-cli
---
## Gotchas
- **`delete_release` takes a numeric `id`, never a tag name.** `delete_tag` is the mirror opposite — it takes the `tag_name` string, never a numeric id. These two tools are asymmetric on purpose; passing a tag name to `delete_release` or a numeric id to `delete_tag` fails. Always resolve the numeric release id via `list_releases` or `get_release` first if you only have a tag name in hand.
- **Deleting a release does not delete its tag.** They are separate destructive operations against separate resources — a release is a wrapper (title, notes, draft/prerelease flags, assets) around a tag, not the tag itself. If the intent is to remove both, call `delete_release` and `delete_tag` separately.
- **`list_releases`/`list_tags` default to `per_page: 20`**, unlike most other gitea-mcp tools which default to 30. There is no auto-pagination in the MCP layer — to get a complete result set, loop `page` upward until a page returns fewer than `per_page` results.
- **`draft`/`is_pre_release` are explicit booleans the caller sets on `create_release` — never inferred from `tag_name`.** Practitioner convention (per the `tea` CLI) uses `-beta`/`-rc` suffixes for prereleases (e.g. `v2.0.0-beta.1`), but Gitea does not enforce or infer this from the tag string. If the user names a tag that looks like a prerelease, set `is_pre_release: true` explicitly rather than assuming the flag is redundant with the name.
- **Tag names are conventionally semver, `v`-prefixed** (`v1.2.0`, `v2.0.0-beta.1`), but this is a practitioner convention, not a Gitea constraint — don't reject or rewrite a caller-supplied tag name that doesn't follow it.
## Dispatch table
| 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. See `references/call-signatures.md` for response shapes.
## Workflow
- [ ] **Creating a release:** Call `create_release` directly with `tag_name` + `target` + `title` — it creates the underlying tag automatically if `tag_name` doesn't already exist, so a separate `create_tag` call is only needed when you want to tag a commit without wrapping it in a release yet. Set `is_pre_release`/`is_draft` explicitly per the Gotchas above; don't leave them to default inference.
- [ ] **Deleting a release safely:** Resolve the numeric id first — call `list_releases` (paginate if needed, see Gotchas) or `get_release` if the id is already known, find the entry matching the target `tag_name`, then call `delete_release` with that `id`. Never pass `tag_name` to `delete_release`.
- [ ] **Deleting a tag along with its release:** Delete the release first (frees the id lookup), then call `delete_tag` with the `tag_name` separately — confirm both are intended before proceeding, since each is an independent irreversible operation.
- [ ] **Listing completely:** If the caller needs all releases or tags (not just the first page), loop `page: 1, 2, 3...` until a response has fewer than `per_page` entries.
If exact response field shapes or additional conventions are needed, read `references/call-signatures.md` and `references/conventions.md`.

View File

@@ -0,0 +1,66 @@
---
topic: call-signatures
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Release and tag call signatures
Verified against the live MCP tool schemas at authoring time (not copied from upstream API docs,
which can drift from the deployed gitea-mcp version). `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 (non-draft, non-prerelease by Gitea's own "latest" definition) release.
**`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)
- If `tag_name` doesn't already exist as a tag, Gitea creates it against `target` as part of this call.
**`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):
```
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.
- Does not delete any release wrapping the tag.
## Pagination
None of the list tools auto-paginate. To collect a full result set, call with `page: 1`, then
`page: 2`, etc., stopping when a page returns fewer items than `per_page`. `list_releases` and
`list_tags` default `per_page` to 20 — lower than the 30-default used by most other gitea-mcp list
tools, so a caller assuming 30 will under-count pages needed for a fixed total.

View File

@@ -0,0 +1,40 @@
---
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 an existing or
newly-created tag. The tag is the git-level object (a name pointing at a commit); the release is a
Gitea-level metadata wrapper around it. This is why `delete_release` and `delete_tag` are separate
calls with separate identifiers (numeric id vs. tag name) — removing the wrapper never implies
removing the underlying pointer, and vice versa.
## 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
`draft` and `is_pre_release`/`prerelease` 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.
## 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.

View File

@@ -0,0 +1,48 @@
# 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/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:**
- SKILL.md (Gotchas — semver tag naming)
- references/conventions.md
**Status:** `extracted`