Files
holocron/plugins/gitea/.apm/skills/gitea-issues/SKILL.md
Defame1297 5e232503c4 feat(kyberforge): execute plugin-to-apm marketplace conversion
Why:
ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace
this repo's hand-authored plugin.json/marketplace.json model, with those
files becoming compiled output of `apm pack` instead of files edited by
hand via the (now-retired) plugin-author/marketplace-author skills.
Issue #90 was the deferred execution of that decision, gated on #88
(apm tooling) and #89 (apm-native agent-author/skill-author routing).

Implementation notes:
- All six plugins (bin, core, git, gitea, kyberforge, lint) now carry
  apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills
  moved with a plain git mv (content-identical across targets). Agents
  were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md
  compiles verbatim to both Claude and Copilot, so plugin-scope agents
  now carry only name/description/model/source_keys -- no tools: field,
  no Claude-only knobs (isolation, maxTurns, effort, memory,
  permissionMode).
- Root apm.yml registers all 7 marketplace packages (6 local plus
  mattpocock-skills as a remote entry) under versioning: per_package,
  matching this repo's existing independent-plugin-versioning practice.
- .claude-plugin/marketplace.json and every plugin's plugin.json are now
  apm-pack-compiled output, verified against the prior hand-maintained
  content: same names/descriptions/versions/licenses/authors, only
  cosmetic serialization differences (JSON key order, owner email vs.
  url, Unicode escaping).
- plugin-author and marketplace-author are retired now that apm-based
  authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0
  for that removal, and the root marketplace catalog bumped
  0.3.1 -> 0.3.2 to match, per the version-bump convention now
  documented in apm-workflow's reference docs instead of a dedicated
  script (apm has no native version-bump automation).
- Fixed hardcoded pre-.apm/ path assumptions across
  .pre-commit-config.yaml, .pre-commit-hooks.yaml,
  scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh,
  scripts/check-vale-style-sync.sh, six plugins' root plugin.json
  (stale skills/hooks/agents pointer fields that check-manifests.sh
  validates), and several tests/*.bats and tests/*.sh fixtures --
  including a bats REPO_ROOT relative-path depth bug (10 files, one
  extra .apm/ directory level to walk up) and a vale probe-path
  isolation regression introduced mid-fix.
- Corrected empirically-wrong assumptions surfaced this session in
  apm-workflow/apm-install's own reference docs: `apm marketplace
  package add` does not accept local paths (only owner/repo remote
  shorthand -- local packages are registered by editing apm.yml's
  marketplace.packages[] directly); `apm compile` is a consumer-side
  AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and
  hard-fails on skill/agent-only packages without --clean; `apm plugin
  init <name>` nests a stray subdirectory when run with a positional
  name arg from inside a same-named directory; no native Copilot
  marketplace output profile exists; .mcp.json is merged into the
  compiled plugin.json content-aware and target-scoped, with no
  dependencies.mcp entry needed for simple passthrough; pipx is the
  correct pip fallback on externally-managed Python environments.
- Renamed agent-author's copilot.agent.md template asset to
  copilot.agent.md.template so apm compile's recursive *.agent.md glob
  stops misparsing the placeholder template as a real agent primitive.

Impact:
plugin.json and marketplace.json are compiled artifacts from here on --
editing them by hand is no longer the workflow; edit apm.yml/.apm/ and
run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries
reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and
ADR-0010 updated for the new .apm/agents/ path (project/user scope
unaffected, per ADR-0016). Full local verification: claude plugin
validate --strict on all 6 plugins, apm audit --ci, apm marketplace
check, check-manifests.sh, and the full test suite (165/165 bats,
13/13 shell scripts) all pass clean.

Fixes: #90
Refs: #88, #89
ADR: 0015
ADR: 0016

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
2026-08-12 18:21:24 +00:00

7.5 KiB

name, description, compatibility, metadata, allowed-tools
name description compatibility metadata allowed-tools
gitea-issues Use when reading or writing Gitea issues: listing repo issues, getting a single issue's details/ comments/labels, creating an issue, updating its state, adding or editing comments, applying labels via issue_write, or searching issues/PRs across repositories. Triggers on "create an issue", "what issues are open", "get issue #N", "close issue #N", "comment on issue #N", "search issues for X" — even when the user doesn't say "Gitea" explicitly. Composes gitea-labels- milestones for all label inference/resolution and milestone lookup — do not use this skill to manage label or milestone definitions themselves (create/edit/delete a label, create/close a milestone), that's gitea-labels-milestones directly. Do not use for pull requests (use gitea-prs) or for local git branch/commit work (use gitea-branches or git-branches). Requires Gitea MCP server configured with write:issue and write:repository token scopes. Requires git remote "origin" pointing to the Gitea instance for owner/repo resolution when invoked directly by a human; an orchestrating caller (e.g. gitea-workflow) may pass owner/repo already resolved.
category version source_keys
integration 0.1.0
gitea-mcp-repo
gitea-mcp-slim-go
context7-websites-gitea
context7-gitea-tea-cli
Bash mcp__gitea__list_issues mcp__gitea__issue_read mcp__gitea__issue_write mcp__gitea__search_issues

Gotchas

  • list_issues has no type or milestones parameter — despite api-reference.md documenting both. The live MCP schema (re-verified via ToolSearch at authoring time — see references/sources.md) only accepts owner, repo (required), state (default "all"), labels (array of label names), since, before (ISO 8601), page, per_page (default 30). This tool provides no way to filter issues-vs-PRs or by milestone. Since issues and PRs share one number space, list_issues results can include PR entries with no client-side filter to exclude them. If you need to know whether a specific number is a PR, call issue_read method: "get" and check is_pull — that field only appears on the single-item response, never in a list item. This exact drift (a prior skill trusted the research doc's type param and broke) is why this skill's reference files were re-verified live rather than copied from api-reference.md.
  • search_issues does have a working type filter ("issues" | "pulls") — unlike list_issues. Its labels parameter is also shaped differently: a comma-separated string, not an array of names.
  • Labels are numeric IDs on write, name strings on read. issue_write's labels parameter (used by add_labels/replace_labels) takes IDs. list_issues/issue_read return names. Never resolve this yourself — compose gitea-labels-milestones (see references/enrichments.md) to get IDs.
  • Milestone on issue_read is {id, title} — an object, not a bare string. This skill only ever needs the id. (The bare-title-string case only happens on the PR side, which is gitea-prs' problem, not this skill's.)
  • Closing-keyword auto-close behavior is plausible but unconfirmed in our research docs. Our research docs confirm Gitea does NOT auto-close an issue on a plain PR merge (unlike GitHub) — closing keywords like Fixes #N/Closes #N in a commit message are not documented one way or the other. After a PR merges (a gitea-prs operation), always re-check the issue's state here via issue_read method: "get" before deciding whether to close it manually — closing an already-closed issue is a harmless no-op, but don't assume a manual close is always needed.
  • Pagination is manual. list_issues and search_issues return one page at a time. Iterate page: 1, 2, ... until the returned count is less than per_page.
  • HTTP 404 may actually mean 403. Gitea hides permission errors as not-found. If a call 404s unexpectedly, verify the token holds write:issue scope (see references/issues.md's Token scope note) before concluding the issue doesn't exist.

Step 1 — Resolve owner and repo

Before any tool call, extract owner and repo from the git remote (skip this if an orchestrating caller already passed them in):

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-issues or /gitea-issues list List issues (optional state filter)
/gitea-issues create Create an issue from conversation context — infers labels, checks milestone fit, applies a configured default assignee if set
/gitea-issues <N> Get issue details (flag it as a PR if is_pull: true)
/gitea-issues <N> comments Get an issue's comments
/gitea-issues close <N> Close an issue
/gitea-issues comment <N> Add a comment from conversation context
/gitea-issues search <query> Cross-repo search via search_issues

For full parameter detail on list_issues/issue_read/issue_write, read references/issues.md. For search_issues, read references/search.md. For the create-flow enrichments (label inference, milestone assignment, assignee workaround, dependency-linking), read references/enrichments.md.

Step 3 — Execute

list (default)

Call list_issues owner: <owner> repo: <repo> state: <"open"|"closed"|"all", default "all">. Remember: results may include PR entries (see Gotchas) — if the caller needs issues only, this tool cannot filter that server-side; note the limitation rather than silently mislabeling PR entries as issues.

create

  1. Extract title and body from conversation context (the most recent task, bug description, or explicit statement). Fall back to an empty body if nothing is available.
  2. Run the enrichment sequence in references/enrichments.md: infer labels (composing gitea-labels-milestones), check for a clearly-fitting open milestone (composing the same skill), and check for a configured default assignee.
  3. Call issue_write method: "create" owner: <owner> repo: <repo> title: <title> body: <body> labels: [<resolved IDs, or omit>] milestone: <resolved ID, or omit> assignees: [<default login, or omit>].
  4. Fire immediately — no confirmation step for the create itself.

<N> (get)

Call issue_read method: "get" owner: <owner> repo: <repo> issue_number: <N>. If is_pull: true, report that this number is actually a PR and suggest gitea-prs for full detail.

<N> comments

Call issue_read method: "get_comments" owner: <owner> repo: <repo> issue_number: <N>.

close <N>

Call issue_write method: "update" owner: <owner> repo: <repo> issue_number: <N> state: "closed". No method: "close" exists.

comment <N>

Extract the comment body from conversation context (same sourcing as create). Call issue_write method: "add_comment" owner: <owner> repo: <repo> issue_number: <N> body: <body>.

search <query>

Call search_issues query: <query>, adding owner, state, type, or labels filters if the request narrows scope (e.g. "search open PRs for X" → type: "pulls" state: "open").

Step 4 — Report

For reads: a compact table or numbered list — number, title, state, labels, milestone.

For writes: confirm what was created/updated with the issue number and URL if returned.

For errors: surface the HTTP code and message; check token scope per the Gotchas if a 404 looks wrong.