refactor(skills): retrofit the corpus to the ADR-0020 context contract (#129)

Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open.

Closes #99, #107, #108, #110, #111, #114, #115, #120.

## The retrofit (waves 1-5)

| | Start | Now |
|---|---|---|
| Description FAILs (>400 chars) | 26 | **0** |
| Body FAILs (>900 words, body-only) | 9 | **0** |
| Dangling routing targets | 2 | **0** |
| `Kyberforge.CompositionNote` | 10 | **0** |
| Preload tax | 21,005 chars | **~10,500** |

Under the 12,000-char success criterion. Per-wave detail is on #99.

## The review fixes

**The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned.

**Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121).

**The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed.

## Verification

- `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed
- `bash tests/run-bats.sh` — 325 tests, 0 failures
- `pre-commit run --all-files` — 17/17
- `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped
- `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only
- Preload tax measured at **10,498 chars**, max description 390 — both inside budget
- Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers

**Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable.

## Version bump

Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries.

Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR.

## Deliberately not in scope

#112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too.

That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix.

Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`.

Co-authored-by: Defame1297 <gitea@rkdr.net>
Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129
Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
This commit was merged in pull request #129.
This commit is contained in:
Claude Code AI - Gitea MCP
2026-09-01 13:47:46 +00:00
committed by Defame1297
parent 0e91a3ae66
commit 598a7c326a
420 changed files with 15303 additions and 4740 deletions

View File

@@ -4,11 +4,17 @@ Read and write Gitea labels and milestones, and resolve label/milestone identity
## What it does
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required for any write, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy. It is a cross-cutting shared skill composed by `gitea-issues` and `gitea-prs`, which call into it for label/milestone resolution before their own `issue_write`/`pull_request_write` calls apply the resolved IDs.
This skill handles label and milestone CRUD (`label_read`/`label_write`, `milestone_read`/`milestone_write`) — listing repo or org labels, creating/editing/deleting a label, resolving a label name to the numeric ID required to apply it to an issue or PR, and listing/creating/updating/closing/deleting a milestone. It also owns label inference: mapping conversation context (bug report, feature request, urgency language) to this repo's `Kind/*`/`Priority/*`/`Status/*` taxonomy.
## Composition
This is a cross-cutting shared skill. `gitea-issues` and `gitea-prs` both compose it whenever they need to apply a label or assign a milestone, rather than duplicating label/milestone logic: they call in for name/title → ID resolution, then their own `issue_write`/`pull_request_write` calls apply the resolved IDs. The split is deliberate — identity resolution lives here once, and the write that attaches an ID to a specific issue or PR lives with the skill that owns that object.
That relationship is documented here rather than in the skill description, which is preloaded into every session and carries routing information only: an agent reaches this skill because the user asked about labels or milestones, not because two other skills call it.
## Usage
```
```text
/gitea-labels-milestones
```

View File

@@ -2,15 +2,10 @@
name: gitea-labels-milestones
description: >
Use when reading or writing Gitea labels or milestones — listing repo/org labels, creating,
editing, or deleting a label, resolving label names to the numeric IDs required for applying
them to an issue or PR, or listing, creating, updating, closing, or deleting a milestone. This is
a cross-cutting shared skill: `gitea-issues` and `gitea-prs` both compose it whenever they need to
apply labels or assign a milestone, rather than duplicating label/milestone logic. Also use for
label inference — mapping a bug report, feature request, or urgency signal in conversation
context to the repo's `Kind/*`/`Priority/*`/`Status/*` label taxonomy. Do not use for applying
already-resolved label IDs or milestone IDs to a specific issue or PR — that write goes through
`issue_write`/`pull_request_write` in `gitea-issues`/`gitea-prs`, not here.
Use when reading or writing Gitea labels or milestones — "create a label", "what labels does
this repo have", "close the milestone" — or to resolve label names to IDs, or infer a
Kind/Priority/Status label from context, even when the user does not say "Gitea".
Not applying them to an issue -> `gitea-issues`. Not to a PR -> `gitea-prs`.
compatibility: Requires Gitea MCP server configured with write:issue and write:repository token scopes.
@@ -21,23 +16,18 @@ metadata:
- gitea-mcp-slim-go
- context7-websites-gitea
- context7-gitea-tea-cli
version: "0.1.1"
version: "0.1.4"
allowed-tools: Bash mcp__gitea__label_read mcp__gitea__label_write mcp__gitea__milestone_read mcp__gitea__milestone_write
---
## Gotchas
- **Label writes take IDs, reads return names.** `label_read` is the only tool that returns full label objects (`id`, `name`, `color`, `description`). Issue/PR responses slim labels down to name strings. Before any label is applied to an issue or PR (in `gitea-issues`/`gitea-prs`), resolve names → IDs here via `label_read method: "list_repo_labels"` — never pass a name string where an ID is expected.
- **Milestones are referenced by ID everywhere, never by title.** `milestone_write` update/delete take `id`. The one place titles show up as the sole handle is the `pull_request_read` response (see next gotcha).
- **Milestone representation differs between issues and PRs.** `issue_read` returns `milestone: {id, title}` — an object. `pull_request_read` returns `milestone: "title string"` — title only, no ID. You cannot recover a milestone ID from a PR response directly; call `milestone_read method: "list"` and match by title instead.
- **Repo labels and org labels are separate pools, never mixed in one call.** `label_read`/`label_write` take either `owner`+`repo` (repo-scoped methods) or `org` (org-scoped methods) — passing both or neither for a given method is a caller error, not something the schema enforces for you. Repo and org labels can both apply to the same issue, but you list/create/edit them through different method values.
- **`milestone_write` accepts `"update"` and `"edit"` as the same operation.** Both method values map to the identical update call. Prefer `"update"` for consistency with `issue_write`/`pull_request_write`.
- **`exclusive` is documented as an org-labels-only flag — it isn't what enforces exclusivity here.** Gitea's docs scope the settable/server-enforced `exclusive` flag to org labels only, and the live `label_write` schema for `create_repo_label`/`edit_repo_label` doesn't document accepting it at all. This repo's `Kind/*`, `Priority/*`, `Status/*` groups still behave as one-label-per-scope, but that's a manually-enforced convention this skill implements client-side, not a guaranteed server behavior for repo labels: applying a new label within a scope (e.g. `Priority/High`) must replace any existing label in that same scope, not add alongside it, and nothing on the server enforces that for you. Label inference (see `references/label-inference.md`) must respect this — replace, don't stack.
- **Pagination is manual on every list call.** `label_read` and `milestone_read` both default to `per_page: 30`. Iterate `page: 1, 2, ...` until the result count is less than `per_page` — there is no cursor or auto-pagination.
- **Schema requiredness differs between the two tool families.** `milestone_read`/`milestone_write` have `owner` and `repo` as hard-required parameters (the call fails validation without them). `label_read`/`label_write` only hard-require `method` — `owner`/`repo`/`org` are functionally required per method but not schema-enforced, so passing none produces a runtime error from Gitea, not a client-side validation error.
- **Applying a label takes a numeric ID, but issue/PR responses slim labels down to name strings.** An issue's existing labels yield no IDs — resolve name → ID with `label_read`.
- **`pull_request_read` returns `milestone` as a bare title string** where `issue_read` returns `{id, title}` — recover the milestone's ID by listing milestones and matching the title.
- **Never assume a `Kind/*`/`Priority/*`/`Status/*` scope is exclusive — read each label's own `exclusive` field.** `list_repo_labels` returns it on every repo label, so it is always *readable* per label; `label_write` documents it as "(org only)" because it is only *settable* through the org create methods. Where it is `true` Gitea enforces one-per-scope itself, and replacing rather than stacking on a label whose `exclusive` is `false` destroys a valid label.
## Step 1 — Resolve owner and repo
## Step 1 — Resolve owner, repo and org
Before any tool call, extract `owner` and `repo` from the git remote (skip this if an orchestrating caller already passed them in):
@@ -47,10 +37,15 @@ git remote get-url origin
If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea remote found — set origin to your Gitea instance URL."
The `*_org_label*` methods take `org`, not `owner`/`repo`. Pass that same `owner` as `org` — it is the org name whenever the owner is an organisation, and the remote URL does not say whether it is one.
Read the failure text before interpreting it. `list_org_labels` needs the `read:organization` token scope, which this skill's declared scopes (`write:issue`, `write:repository`) do not carry, so it fails with `token does not have at least one of required scope(s), required=[read:organization]` *before* it ever determines org-vs-user. Report that: the org pool went unchecked, not empty. Only a not-found response is evidence the owner is a user account with no org pool.
## Step 2 — Dispatch
| Task | Tool | method |
|---|---|---|
| Resolve a label name to its ID | `label_read` | `"list_repo_labels"`, then `"list_org_labels"` |
| List repo labels | `label_read` | `"list_repo_labels"` |
| Get one repo label by ID | `label_read` | `"get_repo_label"` |
| List org labels | `label_read` | `"list_org_labels"` |
@@ -63,6 +58,6 @@ If origin is not set or the URL is not a Gitea URL, stop and report: "No Gitea r
| Update / close a milestone | `milestone_write` | `"update"` |
| Delete a milestone | `milestone_write` | `"delete"` |
For full parameter detail and step-by-step call sequences, read `references/labels.md` (label operations) or `references/milestones.md` (milestone operations). For mapping conversation context to a label to apply, read `references/label-inference.md`.
Every list method paginates manually — `per_page` defaults to 30, so iterate `page: 1, 2, ...` until a page returns fewer results than `per_page`. A truncated list silently breaks name → ID resolution.
Applying resolved label IDs or a milestone ID to a specific issue or PR is out of scope here — that's `issue_write`/`pull_request_write` in the composing skill (`gitea-issues`/`gitea-prs`).
If the task is a label operation, read `references/labels.md`; if a milestone operation, read `references/milestones.md`. If the label to apply has to be derived from conversation context rather than named, read `references/label-inference.md`.

View File

@@ -12,19 +12,20 @@ description) to this repo's `Kind/*` / `Priority/*` / `Status/*` label taxonomy.
`gitea-issues` and `gitea-prs` before creating or updating an issue/PR, and directly when the user
asks to label something without naming exact labels.
## Scoped labels are mutually exclusive — replace, don't stack
## Branching on exclusivity
Each of `Kind/*`, `Priority/*`, `Status/*` is treated as a scoped-label group by convention (the `/`
delimiter naming pattern). Gitea's `exclusive` flag — the mechanism that would let the server itself
enforce one-label-per-scope — is documented as an org-labels-only setting, and the repo-level
`label_write` methods used here don't accept it at all. So exclusivity within these scopes is a
convention this skill enforces client-side, not something the server guarantees: applying a new
label within a scope is expected to replace any existing label in that same scope on the target
issue/PR, not add alongside it. When inference
selects a `Priority/High` label and the issue already carries `Priority/Medium`, the write should
result in only `Priority/High` remaining — use `replace_labels` scoped to that group's labels, or at
minimum remove the superseded label before adding the new one. Never leave two labels from the same
scope applied at once.
`references/labels.md` owns the exclusivity rule and the read-versus-write asymmetry behind it. Read
it there rather than assuming a scope's behaviour from its name. Inference needs only the branch:
carry each candidate label's own `exclusive` value forward from the resolution call and act on it.
- **`exclusive: true`** — the server drops the sibling on write. Add the label and let it; do not
pre-remove the label already there, and do not compute a replacement set client-side. Inferring
`Priority/High` onto an issue carrying `Priority/Medium` needs no special handling.
- **`exclusive: false`** — **add alongside, never replace.** Stripping a co-existing label in the
same scope destroys a valid one: an issue can legitimately carry `Kind/Bug` and `Kind/Security`
at once.
This skill enforces no client-side exclusivity convention of its own.
## Signal → label mapping
@@ -37,6 +38,7 @@ scope applied at once.
| Improvement to existing behavior, "make X better", refactor with behavior change | `Kind/Enhancement` |
| Docs-only change, README/comment/guide updates | `Kind/Documentation` |
| Vulnerability, credential exposure, injection risk, auth bypass | `Kind/Security` |
| Test coverage, "add tests for X", a missing or flaky test, a test-only change | `Kind/Testing` |
**`Priority/*`** (urgency):
@@ -44,6 +46,7 @@ scope applied at once.
|---|---|
| "blocking", "critical", "urgent", production-down | `Priority/Critical` |
| "soon", "high priority", "should do this sprint" | `Priority/High` |
| "low priority", "nice to have", "whenever", explicitly deferred | `Priority/Low` |
| No urgency signal present | `Priority/Medium` (default) |
**`Status/*`** (workflow state):
@@ -52,17 +55,27 @@ scope applied at once.
|---|---|
| Explicit statement that the work is blocked on something else | `Status/Blocked` |
The label names in all three tables are the taxonomy this guide was written against; none of them is
guaranteed to exist on the target repo. Step 2 below resolves every inferred name against the live
label set, and a name that does not resolve is reported rather than substituted.
## Procedure
1. Read the conversation context (issue/PR title, body, or the triggering discussion) for the
signals above.
2. Call `label_read method: "list_repo_labels"` (see `references/labels.md`) to get the current
label set with IDs — inference must never guess an ID, only a name, then resolve it.
3. Match inferred label names against the resolved list (case-insensitive). If a scope group
already has a different label applied on the target and a new one is inferred for that same
scope, plan to replace rather than add (see above).
label set with IDs and each label's `exclusive` value — inference must never guess an ID, only a
name, then resolve it. Both pools can apply to one issue, so for any inferred name absent from
the repo pool, also call `label_read method: "list_org_labels"` with `org` set to the repo's
`owner` before treating it as unresolved. Read that call's failure text: a
`required=[read:organization]` scope error means the org pool was never queried — report the
missing token scope rather than reporting the label unresolved. Only a not-found response means
there is no org pool and the name is genuinely absent.
3. Match inferred label names against the resolved list (case-insensitive) and carry each match's
`exclusive` value forward: `true` means the server replaces the sibling on write, `false` means
the label is added alongside whatever is already applied (see above).
4. **Low-confidence inference omits the label.** If no signal confidently maps to a `Kind/*` value,
do not guess — omit `Kind/*` entirely rather than default to one. `Priority/Medium` is the one
exception: it's the explicit default when no urgency signal is present, not a guess.
5. Hand the resolved IDs (plus which scopes to replace) to the caller's `issue_write`/
5. Hand the resolved IDs, each with its `exclusive` value, to the caller's `issue_write`/
`pull_request_write` call — this skill does not apply labels to an issue or PR itself.

View File

@@ -3,6 +3,7 @@ topic: labels
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
- context7-websites-gitea
---
# Label operations
@@ -37,7 +38,7 @@ or **org-scoped** labels — never both in one call. Pick the method family (`*_
| `name` | string | required for create |
| `color` | string | hex `#RRGGBB`, required for create |
| `description` | string | optional |
| `exclusive` | boolean | org labels only |
| `exclusive` | boolean | accepted as a *write* param on org creates only — repo labels still carry and enforce `exclusive`, set outside this tool surface |
| `is_archived` | boolean | repo labels only |
Note: unlike `milestone_read`/`milestone_write`, `owner`/`repo`/`org` are **not** schema-required on
@@ -46,29 +47,50 @@ runtime error from Gitea rather than a client-side validation error.
## List repo labels
```
```text
label_read method: "list_repo_labels" owner: <owner> repo: <repo> per_page: 50
```
Paginate (`page: 1, 2, ...`) until the returned count is less than `per_page`. This is the only way
to build a complete name → ID map — there is no lookup-by-name endpoint.
Every returned repo label carries its own `exclusive` boolean, so exclusivity is always *readable*
per repo label. That does not contradict `label_write`'s schema, which annotates `exclusive` as
"(org only)": reading and setting are different questions, and only the setting half is org-scoped
(see "Create a label" below). Where the field is `true` Gitea enforces one-label-per-scope
server-side; where it is `false` labels in that scope stack legitimately. Read the field — never
infer exclusivity from the `/` in a name, and never carry another repo's map over.
On the instance this skill was authored against (`Defame1297/holocron`) the split ran: every
`Priority/*`, `Reviewed/*` and `Status/*` label `exclusive: true`, every `Kind/*` label and
`Compat/Breaking` `exclusive: false`. That is one repo's configuration at one point in time, recorded
as a worked example of what the field looks like in practice — it is not a property of the taxonomy
and says nothing about the repo you are called against.
## Get one label
```
```text
label_read method: "get_repo_label" owner: <owner> repo: <repo> id: <id>
```
## Resolve a name to an ID
There is no direct name lookup. List all repo labels (paginating if needed), scan for a
case-insensitive name match, and extract `id`. This is the required first step before any label
application on an issue or PR — the actual `add_labels`/`replace_labels`/`remove_label` call lives
in `gitea-issues`/`gitea-prs` via `issue_write`/`pull_request_write`, which take numeric IDs only.
The tool surface carries no lookup-by-name method. List all repo labels (paginating if needed),
scan for a case-insensitive name match, and extract `id`. Both pools can apply to one issue: if the
name is not in `list_repo_labels`, also check `list_org_labels` before reporting it unresolved. That method
takes `org`, not `owner`/`repo` — pass the repo's `owner` as `org`, which is what it means when the
owner is an organisation. Its failure modes are not interchangeable. `token does not have at least
one of required scope(s), required=[read:organization]` means the org pool was never queried — report
that missing scope rather than reporting the label unresolved. Only a not-found response means the
owner is a user account with no org pool, making the miss a real miss.
Resolution is the required first step before any label application on an issue or PR — the actual
`add_labels`/`replace_labels`/`remove_label` call lives in `gitea-issues`/`gitea-prs` via
`issue_write`/`pull_request_write`, which take numeric IDs only.
## Create a label
```
```text
label_write method: "create_repo_label"
owner: <owner> repo: <repo>
name: "Kind/Bug"
@@ -77,11 +99,14 @@ label_write method: "create_repo_label"
```
For an org label, use `method: "create_org_label"` with `org:` instead of `owner`/`repo`, and
`exclusive: true` if the label belongs to a mutually-exclusive scope group.
`exclusive: true` if the label belongs to a mutually-exclusive scope group. `label_write` accepts
`exclusive` on org methods only, so a repo label's exclusivity cannot be set or cleared through this
tool — it is set in the Gitea UI or against the REST API directly, and read back via
`list_repo_labels`.
## Edit a label
```
```text
label_write method: "edit_repo_label" owner: <owner> repo: <repo> id: <id> color: "#ff0000"
```
@@ -89,7 +114,7 @@ Only pass the fields being changed — `id` plus any of `name`/`color`/`descript
## Delete a label
```
```text
label_write method: "delete_repo_label" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -43,7 +43,7 @@ schema level — there's no scope variant to omit them for.
## List milestones
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> state: "open"
```
@@ -51,7 +51,7 @@ Report each as: id, title, state, due date, open/closed issue counts.
## Get one milestone
```
```text
milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
```
@@ -60,7 +60,7 @@ milestone_read method: "get" owner: <owner> repo: <repo> id: <id>
Needed whenever the only handle available is a title — e.g. a `pull_request_read` response, which
returns `milestone` as a bare title string rather than `{id, title}`. Call:
```
```text
milestone_read method: "list" owner: <owner> repo: <repo> name: <title>
```
@@ -69,7 +69,7 @@ title typo or case mismatch), fall back to listing without the filter and matchi
## Create a milestone
```
```text
milestone_write method: "create"
owner: <owner> repo: <repo>
title: "v1.0"
@@ -82,7 +82,7 @@ this milestone via `issue_write`/`pull_request_write`.
## Update or close a milestone
```
```text
milestone_write method: "update" owner: <owner> repo: <repo> id: <id> state: "closed"
```
@@ -90,7 +90,7 @@ Only pass the fields being changed — `id` plus any of `title`/`description`/`d
## Delete a milestone
```
```text
milestone_write method: "delete" owner: <owner> repo: <repo> id: <id>
```

View File

@@ -21,7 +21,7 @@
- **URL:** context7:/websites/gitea
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — scoped/exclusive label conventions and milestone/label state-transition semantics
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
- **Contributing files:** SKILL.md, references/label-inference.md
- **Contributing files:** SKILL.md, references/labels.md, references/label-inference.md
- **Status:** `extracted`
## context7-gitea-tea-cli