Files
marketplace/skills/use/SKILL.md
T
naudachu 091dceec1d refactor: split issue domain from Gitea transport
An issue was a Gitea row that happened to be cached locally: its identity
was the tracker's number (42.md), its dependencies were tracker numbers
(depends: [#12]), and a local issue existed only as a draft that push
deleted on success. Nothing could be planned or tracked without a tracker.

Split into layers, with knowledge flowing one way:

  skills/issue  DOMAIN  what an issue is: format, validation, dep graph
        ^               offline; stdlib imports only, no subprocess
        | imports
  skills/sync   BRIDGE  map.py    md <-> Gitea JSON, pure, no I/O
                        _gitea.py login pin, api, pagination, filters
  skills/use    REFERENCE  tea CLI docs for non-issue entities

skills/issue never imports skills/sync. Delete the sync layer and the
domain keeps working.

Identity is now a slug derived from the title (wire-sqlc-appclick.md) and
is stable across retitles and pushes. Tracker numbers live in a `gitea:`
field, never in a file name and never in `depends:`; the pair is indexed
in .remote.json, which is a cache over the files, not a second source of
truth.

Behavior changes:

- Pushing is additive. The file is never deleted; it gains gitea:/url:/
  synced: and origin: flips from local to gitea. `origin: local` is a
  durable state, not a pending one.
- Pushes go in topological order so dependencies get numbers first.
- The dependency graph is computed offline from `depends:` metadata; body
  prose is passed through unchanged in both directions rather than being
  rewritten between slugs and #N.
- `origin` is domain-owned (whether work exists elsewhere is a fact about
  the work); the handle and how to reach it stay with sync.

Script moves:

  issue_get.py   -> sync/pull.py
  issue_push.py  -> sync/push.py
  issue_list.py  -> sync/remote.py
  issue_index.py -> issue/issue_index.py
  _tea.py        -> split into issue/issue.py, sync/map.py, sync/_gitea.py

New: issue/issue_new.py, issue/issue_check.py, issue/issue_tree.py, and
sync/comment.py — comment posting was the last issue operation still
hand-rolled through raw `tea api`.

references/issue-format.md moves to skills/issue/references/format.md;
label hex colors move out of it into map.py, since a color is how a
tracker paints a chip, not what an issue is.

Verified: offline path end to end (new, check, tree, index, push
--dry-run) and read-only against Gitea (remote listing, pull with
mapping, comment guard). Write paths of push.py and comment.py are not
exercised here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 23:37:32 +05:00

6.4 KiB


name: use description: Reference docs for the tea CLI — Gitea's command-line client. Load when the user asks about Gitea repos, pulls, releases, milestones, labels, actions, webhooks, or other Gitea entities, to look up the right tea command and flags. Always write the login as the literal placeholder --login "$GITEA_LOGIN" — the tea-guard hook substitutes the operator-pinned login; set it with /tea:auth. Issues are NOT handled here: use /tea:issue to work on them and /tea:sync to move them to and from Gitea.

/tea:use — tea CLI reference

Reference material for the tea CLI (Gitea's official command-line client). Use these docs to look up commands, flags, filters, and output fields before running tea via Bash.

Issues are somewhere else

Do not reach for tea issues or tea api .../issues/... to read or create an issue. Two skills own that, and they keep the payload out of your context:

Skill Scope
/tea:issue issues as units of work — create, read, grep, validate, dependency graph. Offline.
/tea:sync moving issues between the local store and Gitea — pull, push, comment.

This skill covers everything else Gitea has: pulls, releases, milestones, labels, repos, branches, actions, webhooks, notifications, times.

Login: always write the placeholder, never a name (enforced)

Every tea invocation that touches Gitea MUST carry the login as the literal placeholder --login "$GITEA_LOGIN" (or -l "$GITEA_LOGIN"). Do not substitute an actual login name yourself.

The tea-guard PreToolUse hook enforces this and resolves it:

  • no --login → blocked.
  • --login "$GITEA_LOGIN" → the hook reads the operator's pinned login from .claude/settings.local.json (env.GITEA_LOGIN) at call time and rewrites the command to use that literal before it runs.
  • --login <some-name> or any other variable → blocked. You may not choose the login; only the operator does (via /tea:auth).
  • no login pinned → blocked with a pointer to run /tea:auth.

Why: without an explicit login tea silently falls back to the machine's default (possibly the user's personal account), and a login you pick may be the wrong identity. Pinning is the operator's decision; the hook guarantees it. The pin takes effect immediately — no restart. Only tea logins list and tea --version/--help are exempt from the guard.

How to use

  1. Identify the entity in the request: pulls, labels, milestones, releases, times, repos, branches, actions, webhooks, notifications, etc.
  2. Find the matching command in the index below.
  3. Run it via Bash with the placeholder login, e.g. tea pulls list --login "$GITEA_LOGIN" --repo owner/repo --state open. (The hook rewrites "$GITEA_LOGIN" to the operator-pinned login.)

tea auto-detects owner/repo from $PWD inside a git repo; otherwise pass --repo owner/repo (or -r). Login is not auto-detected — it is pinned per-project by the operator (see /tea:auth) and injected by the guard. Config lives in $XDG_CONFIG_HOME/tea.

Index

  • tea CLI overview — global flags, common options, output formats
  • ENTITIES — issues, pulls, labels, milestones, releases, times, repos, branches, actions, webhooks, comment
  • HELPERS — open, notifications, clone, api
  • MISC — whoami, admin
  • SETUP — logins, logout, ssh-keys

The canonical issue format moved to ../issue/references/format.md — it describes local files, not tea commands.

Rich payloads — write to $PWD/tmp/ first, then tea api

Entity subcommands (tea comment, tea pulls create, tea releases create, …) are built for humans at a TTY. With a large or formatted body they can hang silently — an empty-looking positional arg triggers $EDITOR fallback, or a scope/confirm prompt waits on a TTY that doesn't exist. The harness eventually kills the process (e.g. exit 144 = 128 + SIGURG on macOS).

Rule: for any non-trivial body (multi-line, or containing markdown / code fences / backticks / pipes / tables), bypass entity commands. Save the full request payload to $PWD/tmp/ first, then POST via tea api.

Issues and issue comments are already wrapped — use /tea:sync rather than hand-rolling their JSON. The procedure below covers everything else.

Procedure

  1. Ensure the target dir exists: mkdir -p tmp/{kind} where {kind} is pull, release, etc.
  2. Write the complete request body as JSON to $PWD/tmp/{kind}/<slug>.json. One file = one request. Use a quoted heredoc to avoid shell expansion:
    mkdir -p tmp/release
    cat > tmp/release/v0-2-0.json <<'EOF'
    {"tag_name": "v0.2.0", "name": "v0.2.0", "body": "## Changes\n\nMulti-line markdown with `code`."}
    EOF
    
    Newlines inside the body must be encoded as \n in the JSON string. If composing programmatically, pipe through jq -Rs '{body: .}' < body.md > tmp/release/v0-2-0.json.
  3. POST with tea api, passing the file with -d @<path>:
    tea api --login "$GITEA_LOGIN" \
      -X POST -d @tmp/release/v0-2-0.json \
      repos/{owner}/{repo}/releases
    
  4. Keep the file. tmp/ should be gitignored; the saved payload is useful for retries, edits (PATCH), and debugging failed posts.

Common endpoints

Action Method + endpoint
Create PR POST repos/{owner}/{repo}/pulls
Edit PR body or title PATCH repos/{owner}/{repo}/issues/{n}
Comment on a PR POST repos/{owner}/{repo}/issues/{n}/comments
Edit comment PATCH repos/{owner}/{repo}/issues/comments/{id}
Create release POST repos/{owner}/{repo}/releases
Create milestone POST repos/{owner}/{repo}/milestones

Short single-line bodies (e.g. tea comment 42 "lgtm" --login "$GITEA_LOGIN") are still fine via entity commands. Always the placeholder, never a login name.

Tips

  • Pass -o json for structured output when parsing programmatically.
  • Use --fields, -f to narrow columns.
  • Pagination: --page, -p <n> and --limit, --lm <n> (defaults 1 / 30).
  • If a tea command is blocked by tea-guard: either you forgot --login "$GITEA_LOGIN", you wrote a literal login name instead of the placeholder (not allowed — let the guard substitute), or no login is pinned (run /tea:auth).