diff --git a/plugins/gitea/skills/gitea-labels-milestones/README.md b/plugins/gitea/skills/gitea-labels-milestones/README.md new file mode 100644 index 0000000..30fb502 --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/README.md @@ -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 | diff --git a/plugins/gitea/skills/gitea-labels-milestones/SKILL.md b/plugins/gitea/skills/gitea-labels-milestones/SKILL.md new file mode 100644 index 0000000..1c6bc4d --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/SKILL.md @@ -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`). diff --git a/plugins/gitea/skills/gitea-labels-milestones/references/label-inference.md b/plugins/gitea/skills/gitea-labels-milestones/references/label-inference.md new file mode 100644 index 0000000..28b7ebc --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/references/label-inference.md @@ -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. diff --git a/plugins/gitea/skills/gitea-labels-milestones/references/labels.md b/plugins/gitea/skills/gitea-labels-milestones/references/labels.md new file mode 100644 index 0000000..56b129e --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/references/labels.md @@ -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: 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: repo: 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: 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: repo: 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: repo: id: +``` + +Deleting a label does not remove it from historical issue/PR timeline events — it disappears only +from current label lists. diff --git a/plugins/gitea/skills/gitea-labels-milestones/references/milestones.md b/plugins/gitea/skills/gitea-labels-milestones/references/milestones.md new file mode 100644 index 0000000..d87ee72 --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/references/milestones.md @@ -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: repo: state: "open" +``` + +Report each as: id, title, state, due date, open/closed issue counts. + +## Get one milestone + +``` +milestone_read method: "get" owner: repo: 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: repo: name: +``` + +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. diff --git a/plugins/gitea/skills/gitea-labels-milestones/references/sources.md b/plugins/gitea/skills/gitea-labels-milestones/references/sources.md new file mode 100644 index 0000000..0315aac --- /dev/null +++ b/plugins/gitea/skills/gitea-labels-milestones/references/sources.md @@ -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`