Replace fetch_issue.py with four scripts around a flat, greppable cache in tmp/issues/. Planning stays offline and issues reach Gitea in one push: - issue_get.py: fetch by key or by filter (--milestone/--label/-q). The list endpoint carries issue bodies, so a whole milestone costs one request per 50 issues. Gitea silently ignores an unresolvable milestones= filter and returns the entire backlog, so the milestone is resolved up front and every returned issue is re-checked locally. --deps walks the dependency graph downwards via the structured sections plus native dependencies and writes tree-<slug>.md. - issue_push.py: validate a local draft against the canonical format, create missing labels with the right colors and exclusivity, POST, delete the draft. - issue_list.py: discovery to stdout, writes nothing. - issue_index.py: rebuild INDEX.md from what is on disk. Files use one metadata field per line with inline lists so plain grep works without a parser. This is a cache and a drafting area, not a mirror: no drift tracking, no sync back. Projects are not fetchable — the projects API is 404 on Gitea 1.26; documented alongside the milestone caveat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tea — Claude Code plugin for the Gitea CLI
A Claude Code plugin that gives Claude a reference for the tea CLI and enforces a hard rule: every tea command runs under the login the operator chose, never one Claude picked.
What it ships
| Piece | What it does |
|---|---|
/tea:auth skill |
Prompts you to pick a Gitea login and pins it to the project |
/tea:use skill |
Tea CLI reference — loads command docs on demand |
/tea:issue skill |
Drafts issues locally in a canonical format (typed labels, fixed sections), then pushes them |
| Issue scripts | Fetch issues into a flat, greppable local cache (tmp/issues/), walk dependency trees, push drafts |
tea-guard hook |
PreToolUse hook that blocks or rewrites every tea invocation |
Prerequisites
- Claude Code — CLI, desktop app, or IDE extension
- Python 3 — required by the
tea-guardhook (python3must be on$PATH) tea— Gitea's official CLI. Install withbrew install tea(macOS) or from gitea.com/gitea/tea/releases- At least one login configured:
tea logins add(interactive — run it in a terminal, not via Claude)
Installation
This is a Claude Code plugin — install it through the plugin marketplace, not by hand-editing settings.json.
-
Register this repo as a marketplace:
/plugin marketplace add https://git.noodles.cam/claude-skills/tea.gitAlready have a local clone? Point at the directory instead:
/plugin marketplace add /path/to/tea -
Install the plugin:
/plugin install tea@tea
The skills (/tea:auth, /tea:use, /tea:issue) and the tea-guard hook load immediately. Use /plugin to enable, disable, or update it later.
The marketplace registration is written to
extraKnownMarketplacesand the plugin toenabledPluginsin your settings automatically — you don't edit those by hand. There is no top-level"plugins"settings key; if you've added one from older instructions, remove it.
First use
Run /tea:auth once per project. Claude will list your available Gitea logins and ask you to pick one. The choice is written to .claude/settings.local.json and takes effect immediately — no restart needed.
/tea:auth
After that, use /tea:use to look up commands, or just ask Claude to do something with Gitea and it will load the reference automatically.
How the login guard works
Every tea invocation Claude writes must carry the literal placeholder --login "$GITEA_LOGIN". The tea-guard hook intercepts the Bash call before it runs, looks up the pinned login from .claude/settings.local.json, and rewrites the command to use it.
Claude is blocked from:
- running
teawithout--loginat all - naming a login itself (e.g.
--login myaccount) - using any variable other than
$GITEA_LOGIN
This prevents silent fallback to the machine's default login (often a personal account) when working in a project that belongs to a different identity.
tea logins list and tea --version / --help are exempt — they don't touch Gitea data.
Project layout
.claude-plugin/
plugin.json plugin manifest
marketplace.json marketplace catalog (makes `/plugin install` work)
hooks/
hooks.json registers the PreToolUse hook
tea-guard.sh the guard (Python 3, no deps)
skills/
auth/SKILL.md /tea:auth skill
use/SKILL.md /tea:use skill
use/references/tea/ tea CLI reference docs
use/references/issue-format.md canonical issue format (types, templates)
use/scripts/ issue scripts (Python 3, no deps):
issue_get.py fetch issues into tmp/issues/, --deps walks the graph
issue_push.py validate a local draft, create labels, POST, drop the draft
issue_list.py discovery listing to stdout
issue_index.py rebuild tmp/issues/INDEX.md (no network)
_tea.py shared login / api / on-disk-format helpers
issue/SKILL.md /tea:issue skill
Local issue cache
The scripts keep issues in tmp/issues/ (gitignore it) as flat markdown with
one metadata field per line — so grep -l 'labels:.*type/bug' tmp/issues/*.md
works without a parser. It is a cache and a drafting area, not a mirror:
nothing tracks drift and nothing syncs back. Drafts written during planning
live in tmp/issues/drafts/ and are deleted once issue_push.py creates them
in Gitea.