feat: reach the rest of Gitea with kettle api, and drop tea
The plugin required `tea`, Gitea's own CLI, for everything that is not an issue: releases, pull requests, milestones, branches, actions, webhooks. That put a second binary, a second set of logins nothing here could see, and 400 lines documenting somebody else's flags outside anything this repository can test. One command over the transport that already existed removes all three. Transport: `post` — the hand-rolled request the SDK cannot express, written for the dependency endpoint — is generalized to an exported `Do`, and `post` is three lines on top of it. Same http.Client, so the same RoundTripper files the body under .kettle/payload/, the same `token …` header authenticates it, and a non-2xx is the same *APIError. It does not paginate, does not reformat the answer, and names no domain concept, so the layering test is untouched. The endpoint rule is `tea api`'s, so an endpoint table written for that tool still works — with one restriction it did not have: a full URL must be on this instance. Every request carries the project's token in a header, and a URL on another host would hand the token to whatever was typed. Command: `kettle api <endpoint>` in a new `api` group, so the generator writes plugins/kettle/skills/api/SKILL.md — group, directory and /kettle:api are one word. No --repo and no --login, for the reason no sync command has them: a cross-repository address is an address, and another instance is KETTLE_URL. `-X DELETE` needs `--yes`; a flag typed on purpose is an operator's decision. Scopes: a token minted for issues carries write:issue and answers 403 on the first request outside issues, naming no scope. Gitea cannot be asked what a token may do — its own token listing needs a password — so `auth add --scopes` records it, `auth list` and `config` show it, and a 403 says which category it is likely to be. Documentation only; nothing is checked against it. skills/use — the tea reference, 239 lines of it — becomes skills/api: what to ask for, which endpoints paginate, and how to write a body. Every mention of `tea` as a requirement is gone from the manifests, the READMEs, the runner and the four other skills; what survives is the back-compat with the old plugin, which is a decision and not a debt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kettle",
|
||||
"description": "Issues as local markdown, driven by the kettle binary: /kettle:init makes a directory a project, /kettle:issue works on issues offline (format, validation, checkboxes, dependency graph), /kettle:sync moves them to and from Gitea, /kettle:auth manages the credential a project runs under, /kettle:use is the tea CLI reference for the Gitea entities kettle does not cover, and the kettle-runner subagent executes batches on a cheap model. The command reference in each skill is generated from the binary's own command registry, so it cannot drift.",
|
||||
"version": "3.0.0",
|
||||
"description": "Issues as local markdown, driven by the kettle binary: /kettle:init makes a directory a project, /kettle:issue works on issues offline (format, validation, checkboxes, dependency graph), /kettle:sync moves them to and from Gitea, /kettle:auth manages the credential a project runs under, /kettle:api reaches everything else Gitea has — pull requests, releases, tags, milestones, actions, webhooks — through that same login, and the kettle-runner subagent executes batches on a cheap model. The command reference in each skill is generated from the binary's own command registry, so it cannot drift.",
|
||||
"version": "3.1.0",
|
||||
"author": {
|
||||
"name": "naudachu"
|
||||
},
|
||||
|
||||
+18
-11
@@ -31,9 +31,9 @@ go install git.noodles.cam/claude-skills/marketplace/cli/cmd/kettle@latest
|
||||
```
|
||||
|
||||
`cli/go.mod` says **go 1.26** — the Gitea SDK requires it, so that is the minimum
|
||||
for anybody building this. There is no `vendor/`: a build resolves its modules from
|
||||
the module cache or the network, and `go.sum` is what makes that safe. Why that
|
||||
trade was taken is in [`cli/AGENTS.md`](../../cli/AGENTS.md).
|
||||
for anybody building this. `vendor/` is committed, so what compiles is what is in
|
||||
this repository's history; what that does and does not buy is in
|
||||
[`cli/AGENTS.md`](../../cli/AGENTS.md).
|
||||
|
||||
An operator who sees `command not found: kettle` installs it and re-runs; there
|
||||
is nothing to configure in this plugin either way. `kettle config` is the command
|
||||
@@ -63,19 +63,24 @@ the run reports as `without a region` is one where somebody dropped the markers
|
||||
it is left alone, never overwritten, and the fix is to put them back.
|
||||
|
||||
**Groups and skills are not the same set, and that is the one seam.** The binary
|
||||
groups its commands `project`, `issue`, `sync`; the plugin's skills are `init`,
|
||||
`auth`, `project`, `issue`, `sync`, `use`. The generator writes one
|
||||
groups its commands `project`, `issue`, `sync`, `api`; the plugin's skills are
|
||||
`init`, `auth`, `project`, `issue`, `sync`, `api`. The generator writes one
|
||||
`<group>/SKILL.md`, so:
|
||||
|
||||
| skill | generated region | why |
|
||||
|---|---|---|
|
||||
| `issue`, `sync` | yes — the group of the same name | the skill and the group are the same subject |
|
||||
| `issue`, `sync`, `api` | yes — the group of the same name | the skill and the group are the same subject |
|
||||
| `project` | yes — `init`, `auth`, `config`, `gen` | the flag table `init` and `auth` point at |
|
||||
| `init` | no | it is a *procedure* around one command, and it is operator-only |
|
||||
| `auth` | no | it is a *procedure* around two, and it must not tempt a model into typing a token |
|
||||
| `use` | no | it documents `tea`, which is not this binary |
|
||||
|
||||
The three skills with no region hold no flag tables of their own. They name a
|
||||
`api` is the newest of those and the one that shows what the seam is for: the
|
||||
group exists because `kettle api` is not an issue command, and the skill exists
|
||||
because "which endpoint, and does it paginate" is a map somebody loads on its
|
||||
own. The group was named `api` rather than left as `use` precisely so that the
|
||||
three spellings — group, directory, `/kettle:api` — are one word.
|
||||
|
||||
The two skills with no region hold no flag tables of their own. They name a
|
||||
command and send the reader to `/kettle:project`, which is the point: a file that
|
||||
hand-copies a flag list is a file that will disagree with the binary in a month.
|
||||
If the generator ever cannot express what a skill needs, the answer is to change
|
||||
@@ -102,9 +107,10 @@ skills/
|
||||
issue/ SKILL.md /kettle:issue — the offline commands
|
||||
references/format.md THE canonical issue format; source of truth
|
||||
sync/ SKILL.md /kettle:sync — the tracker commands
|
||||
use/ SKILL.md /kettle:use — the `tea` CLI, for the Gitea entities
|
||||
references/tea/ kettle does not cover: releases, webhooks,
|
||||
actions, pull requests
|
||||
api/ SKILL.md /kettle:api — every Gitea entity with no command of
|
||||
its own: pull requests, releases, tags, milestones,
|
||||
branches, actions, webhooks. The map of endpoints,
|
||||
and which of them paginate
|
||||
```
|
||||
|
||||
`references/format.md` is the one document here that the binary does not
|
||||
@@ -138,6 +144,7 @@ and it stays hand-written.
|
||||
| the guard hook and its entry in `hooks/hooks.json` | nothing. It existed to stop a `tea` command running under a login the model picked; the binary holds its own credentials and reads the login out of the project's config, so the failure is not expressible any more |
|
||||
| `.claude/settings.local.json` → `env.GITEA_LOGIN`, the login pin | `<project>/.kettle/config.yaml` (a login **name**) plus `~/.config/kettle/logins.yaml` (the tokens, 0600, outside every working tree) |
|
||||
| the tea plugin's own store marker | `.kettle/`; `kettle init` migrates an older layout in, and each migration is a move |
|
||||
| `tea`, Gitea's own CLI, as an external requirement — a skill of reference docs for somebody else's flags, and a second set of logins nothing here could see | `kettle api`, one request through the transport the binary already had. What was 400 lines documenting another tool is now a map of endpoints; what was two credential stores is one |
|
||||
|
||||
`hooks/agents-sync.sh` is unrelated to any of that and stays. It maintains the
|
||||
`AGENTS.md` convention by repairing the filesystem layout — a real file, with
|
||||
|
||||
+14
-12
@@ -19,7 +19,7 @@ the tool.
|
||||
| `/kettle:project` skill | Generated flag reference for `init`, `auth`, `config`, `gen` |
|
||||
| `/kettle:issue` skill | Issues as units of work — create, read, grep, validate, tick, evict, walk the dependency graph. Entirely offline |
|
||||
| `/kettle:sync` skill | Moves issues between the local store and Gitea — pull, push, comment, close, evict |
|
||||
| `/kettle:use` skill | `tea` CLI reference for the Gitea entities kettle does not cover: pulls, releases, milestones, webhooks, actions |
|
||||
| `/kettle:api` skill | Everything else Gitea has, through `kettle api` and the same login: pull requests, releases, tags, milestones, branches, actions, webhooks, notifications |
|
||||
| `kettle-runner` agent | Subagent on Haiku that runs batches of commands and reports back a receipt — the mechanical half, off your main context |
|
||||
|
||||
## Prerequisites
|
||||
@@ -27,17 +27,18 @@ the tool.
|
||||
- **Claude Code** — CLI, desktop app, or IDE extension.
|
||||
- **The `kettle` binary, on your `PATH`.** It is not installed for you, and
|
||||
nothing here works without it — see below.
|
||||
- **Python 3** — the two `agents-*` hooks are Python scripts; `python3` must be on
|
||||
`$PATH`. Nothing else here needs it.
|
||||
- **`tea`** (optional) — Gitea's own CLI, only for `/kettle:use`. `brew install
|
||||
tea`, or from [gitea.com/gitea/tea/releases](https://gitea.com/gitea/tea/releases).
|
||||
It keeps its own logins (`tea logins add`), separate from kettle's.
|
||||
- **Python 3** — the `agents-sync` hook is a Python script despite its `.sh`
|
||||
name; `python3` must be on `$PATH`. Nothing else here needs it.
|
||||
|
||||
There is **no second CLI to install** — `kettle api`
|
||||
reaches every Gitea entity this plugin has no command for, under the login the
|
||||
project already pins.
|
||||
|
||||
### Installing the binary
|
||||
|
||||
Building it needs **Go 1.26** — `cli/go.mod` says so because the Gitea SDK
|
||||
requires it. There is no `vendor/` directory: the first build downloads eight
|
||||
modules, verified against `go.sum`.
|
||||
requires it. `vendor/` is committed, so a build compiles exactly what is in this
|
||||
repository's history.
|
||||
|
||||
```bash
|
||||
# from a clone of this repository
|
||||
@@ -147,8 +148,10 @@ command yourself; the saving is in the loop, the retry, and reading somebody
|
||||
else's stderr.
|
||||
|
||||
It cannot decide anything. No `Edit`, no `Write`, no `--force`, no widening the
|
||||
set it was handed, no raw `tea`. A missing type, a failed validation, an unpushed
|
||||
dependency come back as a question, not as a guess.
|
||||
set it was handed, no request it composed itself — `kettle api` goes out as the
|
||||
caller spelled it, and a deletion is never something it adds. A missing type, a
|
||||
failed validation, an unpushed dependency come back as a question, not as a
|
||||
guess.
|
||||
|
||||
## Project layout
|
||||
|
||||
@@ -169,8 +172,7 @@ skills/
|
||||
issue/ /kettle:issue — the issue domain, offline
|
||||
references/format.md canonical issue format (identity, types, templates)
|
||||
sync/ /kettle:sync — the bridge to Gitea
|
||||
use/ /kettle:use — tea CLI reference
|
||||
references/tea/ command docs
|
||||
api/ /kettle:api — every other Gitea entity, by endpoint
|
||||
```
|
||||
|
||||
`AGENTS.md` carries the same layout with the reasoning behind it, and the
|
||||
|
||||
@@ -23,6 +23,7 @@ Load the skill, do not remember the flags:
|
||||
`sync-evict`
|
||||
- `/kettle:issue` — `new`, `check`, `ac`, `tree`, `index`, `evict`
|
||||
- `/kettle:project` — `config`, `auth list`
|
||||
- `/kettle:api` — `api`, for the Gitea entities that have no command of their own
|
||||
|
||||
Invoke `Skill` with the one that owns the task at the start and use the generated
|
||||
command reference it carries verbatim. That block is written from the binary's
|
||||
@@ -33,10 +34,13 @@ truth if you need it in a hurry.
|
||||
|
||||
## Hard rules
|
||||
|
||||
1. **`kettle` only.** No raw `tea`, no `tea api`, no curl at a tracker. The
|
||||
binary carries the project's credentials; there is no login for you to name
|
||||
and none for you to choose. If a task needs an entity `kettle` does not cover,
|
||||
that is a finding for the caller, not a command for you to improvise.
|
||||
1. **`kettle` only.** No curl at a tracker, no other CLI, no request you composed
|
||||
yourself. The binary carries the project's credentials; there is no login for
|
||||
you to name and none for you to choose. `kettle api` is a kettle command and
|
||||
is yours to run **as the caller spelled it** — endpoint, method and body come
|
||||
from the task, and a `-X DELETE` is never something you add. An entity nobody
|
||||
named an endpoint for is a finding for the caller, not a request for you to
|
||||
improvise.
|
||||
2. **No writing to issue files.** You have no `Edit` and no `Write`. Commands
|
||||
write files; you do not. If a task needs a body edited or a metadata field
|
||||
changed by hand, stop and say which file and which field. `kettle ac` is the
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
---
|
||||
name: api
|
||||
description: Everything Gitea has that is not an issue — pull requests, releases, tags, branches, milestones, labels, commits, actions, webhooks, notifications, tracked times, users, repositories — reached with `kettle api`, one endpoint per request, under the login the project already pins. Load when the user asks to open or review a PR, cut or edit a release, make a milestone or a tag, look at branches or commits, read notifications or actions, or hit any Gitea endpoint by hand. Issues are NOT handled here: /kettle:issue works on them offline and /kettle:sync moves them to and from the tracker.
|
||||
---
|
||||
|
||||
# /kettle:api — Gitea beyond issues
|
||||
|
||||
`kettle api <endpoint>` is one authenticated request to the Gitea this project is
|
||||
pinned to. No second tool, no second login: the URL, the token and the repository
|
||||
are the ones `/kettle:auth` and `kettle init` already resolved, and the request
|
||||
body is filed under `.kettle/payload/` like every other request kettle makes.
|
||||
|
||||
This skill is the map of what to ask for. `kettle help api` is the flag
|
||||
reference, and it is generated from the binary — the block at the bottom of this
|
||||
file is the same text.
|
||||
|
||||
## Issues are somewhere else
|
||||
|
||||
`kettle api` can reach an issue and must not be used to. An issue read this way
|
||||
comes back as a full JSON payload — every label object, every URL, the whole
|
||||
comment thread — which is precisely what the other two skills exist to keep out
|
||||
of the context window.
|
||||
|
||||
| Skill | Scope |
|
||||
|---|---|
|
||||
| `/kettle:issue` | issues as units of work — create, read, grep, validate, tick, dependency graph. Offline. |
|
||||
| `/kettle:sync` | moving issues between the local store and the tracker — pull, push, comment, close, evict. |
|
||||
|
||||
The one exception is an issue endpoint that is not about the issue's content:
|
||||
`issues/{n}/comments` is also **a pull request's** comment thread, and
|
||||
`PATCH issues/{n}` is also how a pull request's title and body are edited. Gitea
|
||||
numbers issues and pull requests in one sequence and serves both under
|
||||
`/issues/`.
|
||||
|
||||
## What is a command and what is a request
|
||||
|
||||
Reach for the command where there is one: it knows the format, the store and the
|
||||
round trip. Everything else is an endpoint.
|
||||
|
||||
| Subject | How to reach it | Pages? |
|
||||
|---|---|---|
|
||||
| issues (create, read, tick, validate) | `/kettle:issue` — offline, no request at all | — |
|
||||
| issues (pull, push, comment, close, evict) | `/kettle:sync` | handled |
|
||||
| the canonical `type/*` and `severity/*` labels | `kettle labels` | handled |
|
||||
| this repository's own releases, with binaries | `cd cli && make release TAG=v1.2.3` | — |
|
||||
| everything below | `kettle api` | see the column |
|
||||
|
||||
| Entity | Endpoint | Pages? |
|
||||
|---|---|---|
|
||||
| pull requests | `repos/{owner}/{repo}/pulls` | **yes** |
|
||||
| one pull request | `repos/{owner}/{repo}/pulls/{n}` | no |
|
||||
| create a pull request | `POST repos/{owner}/{repo}/pulls` | no |
|
||||
| edit a PR's title or body | `PATCH repos/{owner}/{repo}/issues/{n}` | no |
|
||||
| a PR's or issue's comments | `repos/{owner}/{repo}/issues/{n}/comments` | **yes** |
|
||||
| edit one comment | `PATCH repos/{owner}/{repo}/issues/comments/{id}` | no |
|
||||
| reviews on a PR | `repos/{owner}/{repo}/pulls/{n}/reviews` | **yes** |
|
||||
| merge a PR | `POST repos/{owner}/{repo}/pulls/{n}/merge` | no |
|
||||
| releases | `repos/{owner}/{repo}/releases` | **yes** |
|
||||
| one release by tag | `repos/{owner}/{repo}/releases/tags/{tag}` | no |
|
||||
| tags | `repos/{owner}/{repo}/tags` | **yes** |
|
||||
| branches | `repos/{owner}/{repo}/branches` | **yes** |
|
||||
| commits | `repos/{owner}/{repo}/commits` | **yes** |
|
||||
| milestones | `repos/{owner}/{repo}/milestones` | **yes** |
|
||||
| labels (all of them, not just canonical) | `repos/{owner}/{repo}/labels` | **yes** |
|
||||
| webhooks | `repos/{owner}/{repo}/hooks` | **yes** |
|
||||
| action tasks | `repos/{owner}/{repo}/actions/tasks` | **yes** |
|
||||
| tracked times | `repos/{owner}/{repo}/times` | **yes** |
|
||||
| the repository itself | `repos/{owner}/{repo}` | no |
|
||||
| notifications | `notifications` | **yes** |
|
||||
| who this token is | `user` | no |
|
||||
| an organization's repositories | `orgs/{org}/repos` | **yes** |
|
||||
|
||||
`{owner}` and `{repo}` are filled in from the project's configuration. A path
|
||||
that names a repository in full is left alone — `repos/other-owner/other/releases`
|
||||
reads another repository on the same instance, which is why there is no `--repo`
|
||||
flag. Another **instance** is `KETTLE_URL` and `KETTLE_TOKEN`, not a flag.
|
||||
|
||||
What the instance actually serves is its own version's business; its API docs at
|
||||
`<instance-url>/api/swagger` are the authority when an endpoint answers 404.
|
||||
|
||||
## Pagination is yours
|
||||
|
||||
**One invocation is one HTTP request.** `kettle api` never follows a list to its
|
||||
end, because a passthrough that silently stitched pages together would report as
|
||||
one answer something that was several.
|
||||
|
||||
So for every row marked **yes** above:
|
||||
|
||||
```bash
|
||||
kettle api 'repos/{owner}/{repo}/pulls?state=open&limit=50' # first page, 50 rows
|
||||
kettle api 'repos/{owner}/{repo}/pulls?state=open&limit=50&page=2'
|
||||
```
|
||||
|
||||
- `limit` is capped by the instance (`MAX_RESPONSE_ITEMS`, 50 by default); the
|
||||
default page size is 30.
|
||||
- **A short page is the last one.** Ask for 50, count what came back: fewer than
|
||||
50 means there is no page 3. That is the same rule the binary's own listings
|
||||
use, and it needs no response headers.
|
||||
- Quote any endpoint holding `?` or `&`, or the shell takes it apart.
|
||||
- Walking many pages of anything into your own context is a mistake before it is
|
||||
a request. Narrow the query (`state=`, `since=`, `q=`), or pipe through `jq`
|
||||
and keep the two fields you needed.
|
||||
|
||||
## Writing a body
|
||||
|
||||
Two ways, and the choice is about the body, not the endpoint:
|
||||
|
||||
```bash
|
||||
# small and flat: every value is a string
|
||||
kettle api --field body=lgtm repos/{owner}/{repo}/issues/7/comments
|
||||
|
||||
# anything real — multi-line, markdown, booleans, numbers, nesting
|
||||
mkdir -p tmp/release
|
||||
cat > tmp/release/v0-2-0.json <<'EOF'
|
||||
{"tag_name": "v0.2.0", "name": "v0.2.0", "draft": false,
|
||||
"body": "## Changes\n\nMulti-line markdown with `code`."}
|
||||
EOF
|
||||
kettle api --data @tmp/release/v0-2-0.json repos/{owner}/{repo}/releases
|
||||
```
|
||||
|
||||
- A body implies `POST`; anything else is `-X PUT`, `-X PATCH`, `-X DELETE`.
|
||||
- Newlines inside a JSON string are `\n`. Composing from a file:
|
||||
`jq -Rs '{body: .}' < body.md > tmp/pull/x.json`.
|
||||
- `--field` values are **always strings**. A `draft: false` or a number is a
|
||||
`--data` body — guessing types is how a `tag_name` of `1.0` goes up as a
|
||||
number.
|
||||
- Keep `tmp/` gitignored and keep the file: a `PATCH` is usually the same body
|
||||
with one line changed. `kettle` files its own copy under `.kettle/payload/`
|
||||
automatically; that directory is the transport's and nothing hand-made goes in
|
||||
it.
|
||||
- Attachments are `multipart/form-data` and this command sends JSON. Upload
|
||||
release binaries with the release tooling (`make release`), or the web UI.
|
||||
|
||||
## Deleting
|
||||
|
||||
`-X DELETE` needs `--yes` in the same invocation, and the refusal happens before
|
||||
anything is sent:
|
||||
|
||||
```bash
|
||||
kettle api -X DELETE --yes repos/{owner}/{repo}/releases/12
|
||||
```
|
||||
|
||||
That flag is the whole gate. **Whether a thing should be deleted is the
|
||||
operator's call, not a step in a plan** — ask, do not assume, and never widen a
|
||||
deletion past what was named.
|
||||
|
||||
## When it says 403
|
||||
|
||||
Gitea scopes a token as `<read|write>:<category>`, and a token minted to file
|
||||
issues carries `write:issue` and nothing else. Releases, pull requests, branches,
|
||||
tags and actions are all `repository`, so that token answers **403 on the first
|
||||
`kettle api` outside issues** — and the 403 names no scope.
|
||||
|
||||
`kettle auth list` shows what each login on this machine recorded; `kettle config`
|
||||
shows what this project resolved. Nothing can be read back off the instance
|
||||
(Gitea's own token listing needs a password, not a token), so a scope that was
|
||||
never written down is a scope nobody knows. Minting a new token is the operator's
|
||||
job, in the web UI — `/kettle:auth` has the procedure.
|
||||
|
||||
## What is not an API call at all
|
||||
|
||||
| Want | Do |
|
||||
|---|---|
|
||||
| check out a PR branch, clone, push | `git`. This is git's job and always was |
|
||||
| who am I | `kettle api user` |
|
||||
| open something in a browser | nothing here; hand the user the URL |
|
||||
| add a login, list logins, ssh keys | `/kettle:auth`, and adding one is the operator's |
|
||||
| administer users or the instance | nothing here. Not an agent's work |
|
||||
|
||||
The canonical issue format lives in
|
||||
[`../issue/references/format.md`](../issue/references/format.md) — it describes
|
||||
local files, not requests.
|
||||
|
||||
<!-- kettle:gen -->
|
||||
**Generated from the kettle command registry by `kettle gen skills`.** Everything between the two markers is replaced on the next run — hand-written prose belongs outside them.
|
||||
|
||||
## `kettle api <endpoint>`
|
||||
|
||||
one request to this project's Gitea, for everything that is not an issue
|
||||
|
||||
Releases, pull requests, milestones, branches, tags, actions, webhooks,
|
||||
notifications: everything Gitea has that this binary has no command for. One
|
||||
invocation is ONE request — the credentials, the repository and the payload
|
||||
scratchpad are the ones this project already resolved, so there is nothing to
|
||||
configure and no second tool to log in.
|
||||
|
||||
THE ENDPOINT IS SPELLED THE WAY GITEA'S OWN DOCUMENTATION SPELLS IT. A bare path
|
||||
is taken as relative to `/api/v1/`; a path that already begins `/api/` is sent as it
|
||||
stands, which is how anything outside v1 is reached; a full URL is allowed only
|
||||
on the instance this project points at, because every request here carries the
|
||||
project's token in a header and a URL somewhere else would hand that token over.
|
||||
`{owner}` and `{repo}` are filled in from the project's configuration. Quote an
|
||||
endpoint that contains ? or & or the shell will take it apart.
|
||||
|
||||
ANOTHER REPOSITORY NEEDS NO FLAG — write its address into the path
|
||||
(`repos/other-owner/other-repo/releases`) and nothing is substituted. There is no
|
||||
--repo and no --login here for the same reason there is none on push or pull:
|
||||
which login a project runs under is a fact about the project. Another INSTANCE
|
||||
is KETTLE_URL and KETTLE_TOKEN, which is also what a CI run uses.
|
||||
|
||||
THE ANSWER IS THE SERVER'S BYTES ON STDOUT, unparsed and unreformatted — pipe it
|
||||
to jq, redirect it to a file. There is no flag that names an output file: in
|
||||
this tree --out is the issue store, and one word meaning two things is exactly
|
||||
the trap the tool this replaces set with an -o that wrote a file called "json".
|
||||
|
||||
IT DOES NOT PAGINATE. One call is one request, so a listing answers with one
|
||||
page: ask for the next with ?page=2, and for a bigger one with ?limit=50 (the
|
||||
server's own default is 30, its maximum is usually 50). A passthrough that
|
||||
stitched pages together silently would report as one answer something that was
|
||||
several.
|
||||
|
||||
ISSUES ARE NOT THIS COMMAND'S JOB even though it can reach them. An issue read
|
||||
this way arrives as a full JSON payload — every comment, every label object,
|
||||
every URL — which is what /kettle:issue and /kettle:sync exist to keep out of a
|
||||
context window. Use pull, push, comment and close.
|
||||
|
||||
A 403 here is usually the token rather than the request: a token minted for
|
||||
issues carries write:issue, and releases, pull requests, branches and tags are
|
||||
all under repository. `kettle auth list` shows what each login records.
|
||||
|
||||
-X DELETE NEEDS --yes. Everything else goes through as typed; a deletion does
|
||||
not, because a flag typed on purpose is an operator's decision and the URL of a
|
||||
release is one character away from the URL of the wrong release.
|
||||
|
||||
What it cannot do: an upload. Release attachments are multipart/form-data and
|
||||
this sends JSON — the release tooling in cmd/release does those.
|
||||
|
||||
| flag | default | what it does |
|
||||
| --- | --- | --- |
|
||||
| `--X` | — | the same flag as --method, spelled the way curl and the tool this replaces spell it |
|
||||
| `--data` | — | the request body: @file, @- for standard input, or the JSON itself |
|
||||
| `--field` | — | key=value, added to a JSON body as a string; repeatable |
|
||||
| `--method` | — | GET, POST, PUT, PATCH or DELETE (default GET, or POST when there is a body) |
|
||||
| `--status` | `false` | print the status line on standard error |
|
||||
| `--yes` | `false` | confirm a DELETE |
|
||||
|
||||
```bash
|
||||
kettle api repos/{owner}/{repo}/releases # the latest page of releases, as JSON
|
||||
kettle api user # who this project's token belongs to
|
||||
kettle api 'repos/{owner}/{repo}/pulls?state=open&limit=50' # quote anything with ? or & in it
|
||||
kettle api --data @tmp/release/v0-2-0.json repos/{owner}/{repo}/releases # a body from a file; POST is implied
|
||||
kettle api --field body=lgtm repos/{owner}/{repo}/issues/7/comments # a small body without a file
|
||||
kettle api -X DELETE --yes repos/{owner}/{repo}/releases/12 # a deletion, said out loud
|
||||
kettle api repos/{owner}/{repo}/milestones | jq '.[].title' # the bytes are the server's; jq is yours
|
||||
```
|
||||
<!-- /kettle:gen -->
|
||||
@@ -85,9 +85,33 @@ repo claude-skills/marketplace
|
||||
| `401` / `403` from a sync command | report it verbatim. Do **not** try another login, and do not edit or remove one to route around it — that is somebody's identity, not a setting |
|
||||
| `token none` in `kettle config` | a name is pinned but no credential answers to it |
|
||||
|
||||
**`tea` does not read any of this.** The `tea` CLI keeps its own configuration
|
||||
under `$XDG_CONFIG_HOME/tea` and its own logins (`tea logins list`), and
|
||||
configuring one tool configures nothing in the other — see `/kettle:use`.
|
||||
## Scopes: what the token is allowed to do
|
||||
|
||||
Gitea mints a token with scopes, spelled `<read|write>:<category>`. A token made
|
||||
for issues carries `write:issue` — and that is enough for everything
|
||||
`/kettle:issue` and `/kettle:sync` do, and **not** enough for anything
|
||||
`/kettle:api` reaches: releases, pull requests, branches, tags and actions all
|
||||
sit under `repository`.
|
||||
|
||||
| doing | needs |
|
||||
|---|---|
|
||||
| pull, push, comment, close, evict | `write:issue` |
|
||||
| `kettle labels` | `write:issue` |
|
||||
| `kettle api` on releases, PRs, tags, branches, actions | `write:repository` too |
|
||||
| reading any of those without writing | the `read:` half is enough |
|
||||
|
||||
`kettle auth add --scopes write:issue,write:repository` writes that down beside
|
||||
the login. **It is a note and nothing else** — nothing is checked against it and
|
||||
nothing is refused because of it. It is worth writing down because the instance
|
||||
will not answer the question: Gitea's own token listing needs a password rather
|
||||
than a token, so a token cannot be asked what it may do. `kettle auth list` and
|
||||
`kettle config` show what was recorded; `(not recorded)` means nobody wrote it
|
||||
down, never "none".
|
||||
|
||||
A **403** from a sync command or from `kettle api` is usually this and says so.
|
||||
Minting a new token is the operator's job in the web UI — the same flow as step
|
||||
2 above, with both scopes ticked this time. Never remove or re-point a login to
|
||||
route around a 403.
|
||||
|
||||
**No `kettle` on PATH?** `command not found: kettle` is the whole story. Stop and
|
||||
tell the operator to install it: `cd cli && go build -o ~/.local/bin/kettle
|
||||
|
||||
@@ -20,7 +20,8 @@ an issue. It is the single source of truth for identity, metadata, types,
|
||||
labels, templates, and language rules.
|
||||
|
||||
**No `kettle` on PATH?** `command not found: kettle` is the whole story — the
|
||||
Python scripts this plugin used to ship are gone and `tea` is not a substitute.
|
||||
Python scripts this plugin used to ship are gone and no other CLI is a
|
||||
substitute.
|
||||
Stop and tell the operator to install it: `cd cli && go build -o
|
||||
~/.local/bin/kettle ./cmd/kettle` in the marketplace repository (go.mod requires
|
||||
**go 1.26**), or `go install
|
||||
|
||||
@@ -43,10 +43,10 @@ milestone: v0.2
|
||||
depends: [migrate-schema]
|
||||
origin: gitea
|
||||
branch: feat/wire-sqlc
|
||||
gitea: claude-skills/tea#42
|
||||
gitea: claude-skills/marketplace#42
|
||||
remote-updated: 2026-08-09T18:24:01Z
|
||||
synced: 2026-08-09T18:40:00Z
|
||||
url: https://git.noodles.cam/claude-skills/tea/issues/42
|
||||
url: https://git.noodles.cam/claude-skills/marketplace/issues/42
|
||||
---
|
||||
# Wire sqlc into the appclick repo layer
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ nothing on its own, which is what makes it safe inside a working tree.
|
||||
`KETTLE_URL` and `KETTLE_TOKEN` each override the file they shadow.
|
||||
|
||||
**No `kettle` on PATH?** `command not found: kettle` is the whole story — no
|
||||
script and no `tea` invocation substitutes for it. Stop and tell the operator to
|
||||
script and no other CLI substitutes for it. Stop and tell the operator to
|
||||
install it: `cd cli && go build -o ~/.local/bin/kettle ./cmd/kettle` in the
|
||||
marketplace repository (go.mod requires **go 1.26**), or
|
||||
`go install git.noodles.cam/claude-skills/marketplace/cli/cmd/kettle@latest`.
|
||||
@@ -47,9 +47,19 @@ argument is in the shell history the moment it is typed:
|
||||
|
||||
`list` never prints a token. There is no flag to make it.
|
||||
|
||||
--scopes RECORDS WHAT THE TOKEN WAS MINTED WITH, and records is all it does:
|
||||
nothing is checked against it and nothing is refused because of it. It is worth
|
||||
writing down because the instance will not answer the question — Gitea's own
|
||||
token listing needs a password, not a token, so a token cannot be asked what it
|
||||
may do. Gitea spells them <read|write>:<category>; issues need `write:issue`,
|
||||
and everything `kettle api` reaches outside issues — releases, pull requests,
|
||||
branches, tags, actions — is `repository`. A token minted for issues alone
|
||||
answers 403 there, and the 403 names no scope.
|
||||
|
||||
| flag | default | what it does |
|
||||
| --- | --- | --- |
|
||||
| `--name` | — | login name (add) |
|
||||
| `--scopes` | — | what the token was minted with, comma separated, e.g. write:issue,write:repository; documentation only (add) |
|
||||
| `--token` | — | token, if you would rather not use stdin (add) |
|
||||
| `--url` | — | instance URL, e.g. https://git.example.com (add) |
|
||||
| `--user` | — | account this token belongs to; documentation only (add) |
|
||||
@@ -57,6 +67,7 @@ argument is in the shell history the moment it is typed:
|
||||
```bash
|
||||
kettle auth list # what this machine holds
|
||||
pass show gitea | kettle auth add --name noodles --url https://git.example.com # add one, token on stdin
|
||||
kettle auth add --name noodles --url https://git.example.com --scopes write:issue,write:repository < t.txt # and write down what it can do
|
||||
kettle auth remove noodles # forget it
|
||||
```
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ second-guesses it. Knowledge flows one way: delete the tracker from the world an
|
||||
the issue domain does not notice.
|
||||
|
||||
**No `kettle` on PATH?** `command not found: kettle` is the whole story — the
|
||||
Python scripts this plugin used to ship are gone and raw `tea` is not a
|
||||
Python scripts this plugin used to ship are gone and no other CLI is a
|
||||
substitute. Stop and tell the operator to install it: `cd cli && go build -o
|
||||
~/.local/bin/kettle ./cmd/kettle` in the marketplace repository (go.mod requires
|
||||
**go 1.26**), or `go install
|
||||
@@ -38,11 +38,12 @@ No login, an unknown login name, a 401: report it and stop — `/kettle:auth`.
|
||||
|
||||
## Never read an issue through a raw API dump
|
||||
|
||||
`tea issues 42 -o json` and `tea api …/issues/42` put the whole payload —
|
||||
avatars, nested user objects, every comment body — into your context whether you
|
||||
need it or not. `kettle pull` writes flat markdown and prints a compact line per
|
||||
issue; `kettle remote` lists the tracker without writing anything at all. Use
|
||||
those.
|
||||
`kettle api repos/{owner}/{repo}/issues/42` will answer, and answering is the
|
||||
problem: the whole payload — avatars, nested user objects, every comment body —
|
||||
lands in your context whether you need it or not. `kettle pull` writes flat
|
||||
markdown and prints a compact line per issue; `kettle remote` lists the tracker
|
||||
without writing anything at all. Use those. `/kettle:api` is for the entities
|
||||
that have no command, and it says the same thing from its side.
|
||||
|
||||
## The round trip is one rule
|
||||
|
||||
@@ -183,9 +184,9 @@ request bodies are debris of the transport, and a scratchpad inside a store make
|
||||
`ls .kettle/issues` lie about what exists. Nothing in it is anybody's only copy —
|
||||
deleting it costs nothing. A run that sends nothing leaves no directory behind.
|
||||
|
||||
For Gitea entities `kettle` does not cover — releases, webhooks, actions, pull
|
||||
requests — the tool is `tea`, and it keeps its own configuration and its own
|
||||
logins. `/kettle:use`.
|
||||
For Gitea entities `kettle` has no command for — releases, webhooks, actions,
|
||||
pull requests — `kettle api` sends the request under this same login, into this
|
||||
same scratchpad. `/kettle:api`.
|
||||
|
||||
The commands themselves follow. Their usage lines, flags, defaults and examples
|
||||
are generated from the binary's own command registry, so they cannot disagree
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
---
|
||||
name: use
|
||||
description: Reference docs for the `tea` CLI — Gitea's own command-line client, and the way to reach every Gitea entity the `kettle` binary does not cover. Load when the user asks about pull requests, releases, milestones, labels, repos, branches, actions, webhooks, notifications or times, to look up the right `tea` command and flags. `tea` keeps its own configuration and its own logins, entirely separate from kettle's. Issues are NOT handled here: /kettle:issue works on them offline and /kettle:sync moves them to and from the tracker.
|
||||
---
|
||||
|
||||
# /kettle:use — tea CLI reference
|
||||
|
||||
Reference material for `tea`, Gitea's official command-line client. Use these
|
||||
docs to look up commands, flags, filters and output fields before running `tea`
|
||||
via Bash.
|
||||
|
||||
`kettle` covers issues and nothing else. Everything else Gitea has — pulls,
|
||||
releases, milestones, labels, repos, branches, actions, webhooks, notifications,
|
||||
times — is reached through `tea`, and this skill is how.
|
||||
|
||||
## 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 |
|
||||
|---|---|
|
||||
| `/kettle:issue` | issues as units of work — create, read, grep, validate, tick, dependency graph. Offline. |
|
||||
| `/kettle:sync` | moving issues between the local store and the tracker — pull, push, comment, close, evict. |
|
||||
|
||||
## Login: `tea` has its own configuration, and it is not kettle's
|
||||
|
||||
Two tools, two credential stores, no connection between them:
|
||||
|
||||
| tool | where its logins live | how they are managed |
|
||||
|---|---|---|
|
||||
| `tea` | `$XDG_CONFIG_HOME/tea` | `tea logins list`, `tea logins add` (interactive), `tea logins default` |
|
||||
| `kettle` | `~/.config/kettle/logins.yaml` + `<project>/.kettle/config.yaml` | `kettle auth`, `kettle init --login` (`/kettle:auth`) |
|
||||
|
||||
**Configuring one configures nothing in the other.** `/kettle:auth` does not give
|
||||
`tea` a credential, and `tea logins add` does not give `kettle` one. A project
|
||||
whose `kettle` commands work fine can still have no `tea` login at all, and the
|
||||
error you get will be about the login `tea` chose for itself.
|
||||
|
||||
**There is no `$GITEA_LOGIN` placeholder and no hook that substitutes one.** The
|
||||
PreToolUse guard that used to rewrite it was deleted along with the Python
|
||||
scripts; writing `--login "$GITEA_LOGIN"` now passes an empty variable to `tea`
|
||||
and fails in a way that reads like a `tea` bug. If you find that spelling
|
||||
anywhere, it is stale.
|
||||
|
||||
How to name a login honestly:
|
||||
|
||||
- Inside a checkout, `tea` auto-detects owner, repo and login from the git
|
||||
remote. That is usually right and usually enough — run the command without
|
||||
`--login`.
|
||||
- When the machine holds more than one login, or you are outside a checkout,
|
||||
pass `--login <name>` with a name out of `tea logins list`. **Which one is the
|
||||
operator's call**: ask with `AskUserQuestion` rather than picking the one that
|
||||
looks likely. A wrong identity writes to a real tracker under somebody else's
|
||||
account.
|
||||
- `no gitea login detected, falling back to login '…'` is a **hard failure**, not
|
||||
a warning. Stop, do not act on the result, surface the line.
|
||||
- **Never mutate login state**: no `tea logins add/edit/delete/default`, no
|
||||
`tea logout`. `tea logins list` is the only login command that is yours to run,
|
||||
and adding a login is interactive — the operator does it in their own terminal.
|
||||
|
||||
## 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, e.g. `tea pulls list --repo owner/repo --state open`.
|
||||
|
||||
`tea` auto-detects owner/repo from `$PWD` inside a git repo; otherwise pass
|
||||
`--repo owner/repo` (or `-r`).
|
||||
|
||||
### `--repo` takes a slug — except where a checkout is required
|
||||
|
||||
A few commands touch local git, not just the API, and for those `--repo` **must
|
||||
be a path to a checkout**; a slug is rejected:
|
||||
|
||||
```
|
||||
Error: local repository required: execute from a repo dir, or specify a path with --repo
|
||||
```
|
||||
|
||||
The message reads like the flag is missing even when it was passed. Confirmed
|
||||
for `pulls create`, `pulls checkout` and `pulls clean` (tea 0.14.x). Everything
|
||||
that is only an API call — `pulls list`, `milestones`, `releases`, `times`,
|
||||
`labels`, `issues` — takes the slug from any directory.
|
||||
|
||||
Three working forms for `pulls create`:
|
||||
|
||||
```bash
|
||||
# 1. cwd inside the checkout, no --repo at all
|
||||
tea pulls create --head feat/x --base main --title "…" --description "…"
|
||||
|
||||
# 2. from anywhere, --repo as a PATH (this is also the git-worktree answer:
|
||||
# point it at the main checkout)
|
||||
tea pulls create --repo /path/to/checkout \
|
||||
--head feat/x --base main --title "…" --description "…"
|
||||
|
||||
# 3. no checkout in reach — POST it, where owner/repo is a slug again
|
||||
tea api -X POST -d @tmp/pull/x.json repos/{owner}/{repo}/pulls
|
||||
```
|
||||
|
||||
## Index
|
||||
|
||||
- [tea CLI overview](references/tea/index.md) — global flags, common options, output formats
|
||||
- [ENTITIES](references/tea/entities.md) — issues, pulls, labels, milestones, releases, times, repos, branches, actions, webhooks, comment
|
||||
- [HELPERS](references/tea/helpers.md) — open, notifications, clone, api
|
||||
- [MISC](references/tea/misc.md) — whoami, admin
|
||||
- [SETUP](references/tea/setup.md) — logins, logout, ssh-keys
|
||||
|
||||
The canonical issue format lives in
|
||||
[`../issue/references/format.md`](../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 the `$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 `/kettle:sync` rather than
|
||||
hand-rolling their JSON. `.kettle/payload/` is kettle's own scratchpad and is
|
||||
written by kettle only; do not put hand-made bodies there. 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:
|
||||
```bash
|
||||
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>`:
|
||||
```bash
|
||||
tea api -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"`) are still fine via
|
||||
entity commands.
|
||||
|
||||
## Tips
|
||||
|
||||
- Pass `-o json` for structured output when parsing programmatically — on
|
||||
**entity commands only**. On `tea api`, `-o` is a *file name*: `-o json`
|
||||
writes the response body to a file called `json` and leaves stdout empty.
|
||||
The response is already JSON, so there is nothing to format; use `-` for
|
||||
stdout, or leave the flag off.
|
||||
- Use `--fields, -f` to narrow columns.
|
||||
- Pagination: `--page, -p <n>` and `--limit, --lm <n>` (defaults 1 / 30).
|
||||
- A `tea` command that fails on identity is a login problem in **tea's** own
|
||||
config, never in kettle's — `tea logins list`, and the operator decides.
|
||||
@@ -1,137 +0,0 @@
|
||||
# tea CLI — ENTITIES
|
||||
|
||||
See [`./index.md`](./index.md) for global options and common flags shared by all commands.
|
||||
|
||||
## `tea issues` (aliases: `issue`, `i`)
|
||||
Without args lists issues; with `<index>` shows issue detail.
|
||||
|
||||
Shared filters: `--state {all|open|closed}` (default: open), `--kind {issues|pulls|all}`, `--keyword/-k`, `--labels/-L`, `--milestones/-m`, `--author/-A`, `--assignee/-a`, `--mentions/-M`, `--owner/--org`, `--from/-F`, `--until/-u`, `--comments`. Available fields: `index,state,kind,author,author-id,url,title,body,created,updated,deadline,assignees,milestone,labels,comments,owner,repo`.
|
||||
|
||||
Subcommands:
|
||||
- `list, ls` — list (same filters as above).
|
||||
- `create, c` — create an issue. Options: `--title/-t`, `--description/-d`, `--assignees/-a`, `--labels/-L`, `--milestone/-m`, `--deadline/-D`, `--referenced-version/-v` (commit hash or tag).
|
||||
- `edit, e <idx>...` — edit. `--title`, `--description`, `--add-assignees/-a`, `--add-labels/-L`, `--remove-labels`, `--milestone`, `--deadline`, `--referenced-version`. To unset a value pass an empty string (`--milestone ""`).
|
||||
- `reopen, open <idx>...`
|
||||
- `close <idx>...`
|
||||
|
||||
## `tea pulls` (aliases: `pull`, `pr`)
|
||||
Without args lists PRs; with `<index>` shows PR detail. Fields: `index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments,ci`.
|
||||
|
||||
Subcommands:
|
||||
- `list, ls` (`--state`)
|
||||
- `checkout, co <idx>` — check out PR locally. `--branch/-b` creates a local branch if missing. Needs a checkout, same as `create`: `--repo` is a path here, not a slug.
|
||||
- `clean <idx>` — delete local and remote feature branches for a closed PR. `--ignore-sha` matches branch by name instead of commit hash. Needs a checkout, same as `create`.
|
||||
- `create, c` — create a PR. `--head <user:branch>`, `--base/-b`, `--allow-maintainer-edits/--edits`, `--agit`, `--topic`, plus all issue-style fields (`--title`, `--description`, `--assignees`, `--labels`, `--milestone`, `--deadline`, `--referenced-version`).
|
||||
**Needs a local checkout.** `--repo owner/repo` is *not* accepted here — the
|
||||
slug fails with `local repository required: execute from a repo dir, or
|
||||
specify a path with --repo`, whose advice reads like the flag was missing.
|
||||
Run it with cwd inside the checkout and no `--repo`, or pass `--repo
|
||||
/path/to/checkout`. From a git worktree, point `--repo` at the main
|
||||
checkout. With no checkout in reach, `POST repos/{owner}/{repo}/pulls`
|
||||
through `tea api`, which takes the slug.
|
||||
- `close <idx>...`, `reopen, open <idx>...`
|
||||
- `edit, e <idx>...` — like `issues edit` plus `--add-reviewers/-r`, `--remove-reviewers`.
|
||||
- `review <idx>` — interactive review.
|
||||
- `approve, lgtm, a <idx> [comment]`
|
||||
- `reject <idx> <reason>`
|
||||
- `merge, m <idx>` — `--style/-s {merge|rebase|squash|rebase-merge}` (default merge), `--title/-t`, `--message/-m`.
|
||||
- `review-comments, rc <idx>` — list review comments. Fields: `id,body,reviewer,path,line,resolver,created,updated,url`.
|
||||
- `resolve <comment-id>` / `unresolve <comment-id>`
|
||||
|
||||
## `tea labels` (alias: `label`)
|
||||
- `list, ls` — `--save/-s` dumps labels to a file.
|
||||
- `create, c` — `--name`, `--color`, `--description`, `--file` (bulk import from file).
|
||||
- `update` — `--id`, `--name`, `--color`, `--description`.
|
||||
- `delete, rm` — `--id`.
|
||||
|
||||
## `tea milestones` (aliases: `milestone`, `ms`)
|
||||
Fields: `title,state,items_open,items_closed,items,duedate,description,created,updated,closed,id`.
|
||||
|
||||
- `list, ls` (`--state`)
|
||||
- `create, c` — `--title/-t`, `--description/-d`, `--deadline/--expires/-x`, `--state`.
|
||||
- `close <name>...` — `--force/-f` deletes instead of closing.
|
||||
- `reopen, open <name>...`
|
||||
- `delete, rm <name>`
|
||||
- `issues, i <name>` — manage milestone contents:
|
||||
- `add, a <name> <issue-idx>`
|
||||
- `remove, r <name> <issue-idx>`
|
||||
|
||||
## `tea releases` (aliases: `release`, `r`)
|
||||
- `list, ls`
|
||||
- `create, c [<tag>]` — `--tag`, `--target` (branch/commit), `--title/-t`, `--note/-n`, `--note-file/-f`, `--draft/-d`, `--prerelease/-p`, `--asset/-a <path>` (repeatable).
|
||||
- `edit, e <tag>...` — `--tag`, `--target`, `--title/-t`, `--note/-n`, `--draft/-d <bool>`, `--prerelease/-p <bool>`.
|
||||
- `delete, rm <tag>...` — `--confirm/-y` required; `--delete-tag` also removes the git tag.
|
||||
- `assets, asset, a` — manage release attachments:
|
||||
- `list, ls <tag>`
|
||||
- `create, c <tag> <asset>...`
|
||||
- `delete, rm <tag> <attachment-name>...` — `--confirm/-y`.
|
||||
|
||||
## `tea times` (aliases: `time`, `t`)
|
||||
Time tracking on issues/PRs. Fields: `id,created,repo,issue,user,duration`. Command-level: `--from/-f`, `--until/-u`, `--total/-t`, `--mine/-m`.
|
||||
|
||||
- `add, a <issue> <duration>` — e.g. `tea times add 1 1h25m`.
|
||||
- `delete, rm <issue> <time-id>`
|
||||
- `reset <issue>`
|
||||
- `list, ls [username | #issue]` — username filters by user on the repo; `#N` filters by issue; `--mine` aggregates across all repos.
|
||||
|
||||
## `tea organizations` (aliases: `organization`, `org`)
|
||||
- `list, ls`
|
||||
- `create, c <name>` — `--full-name/-n`, `--description/-d`, `--website/-w`, `--location/-L`, `--visibility/-v`, `--repo-admins-can-change-team-access`.
|
||||
- `delete, rm <name>`
|
||||
|
||||
## `tea repos` (alias: `repo`)
|
||||
Fields: `description,forks,id,name,owner,stars,ssh,updated,url,permission,type`.
|
||||
|
||||
- `list, ls` — `--watched/-w`, `--starred/-s`, `--owner/-O`, `--type/-T {fork|mirror|source}`.
|
||||
- `search, s [term]` — `--topic/-t`, `--type/-T`, `--owner/-O`, `--private {true|false}`, `--archived {true|false}`.
|
||||
- `create, c` — `--name`, `--owner/-O`, `--private`, `--description/--desc`, `--init`, `--labels`, `--gitignores/--git`, `--license`, `--readme`, `--branch`, `--template`, `--trustmodel {committer|collaborator|collaborator+committer}`, `--object-format {sha1|sha256}`.
|
||||
- `create-from-template, ct` — `--template/-t`, `--name/-n`, `--owner/-O`, `--private`, `--description/--desc`, copy toggles: `--content`, `--githooks`, `--avatar`, `--labels`, `--topics`, `--webhooks`.
|
||||
- `fork, f` — `--owner/-O` (default: current user).
|
||||
- `migrate, m` — `--name`, `--owner`, `--clone-url`, `--service {git|gitea|gitlab|gogs}`, `--mirror`, `--mirror-interval`, `--private`, `--template`, copy toggles: `--wiki`, `--issues`, `--labels`, `--pull-requests`, `--releases`, `--milestones`, `--lfs`, `--lfs-endpoint`, auth: `--auth-user`, `--auth-password`, `--auth-token`.
|
||||
- `delete, rm` — `--name`, `--owner/-O`, `--force/-f`.
|
||||
- `edit, e` — `--name`, `--description/--desc`, `--website`, `--private <bool>`, `--template <bool>`, `--archived <bool>`, `--default-branch`.
|
||||
|
||||
## `tea branches` (aliases: `branch`, `b`)
|
||||
Fields: `name,protected,user-can-merge,user-can-push,protection`.
|
||||
|
||||
- `list, ls`
|
||||
- `protect, P <branch>` — enable branch protection.
|
||||
- `unprotect, U <branch>` — remove protection.
|
||||
- `rename, rn <old> <new>`
|
||||
|
||||
## `tea actions` (alias: `action`)
|
||||
CI management: secrets, variables, workflow definitions, workflow runs.
|
||||
|
||||
### `tea actions secrets` (alias: `secret`)
|
||||
- `list, ls`
|
||||
- `create, add, set <name> [value]` — `--file` or `--stdin` to read the value.
|
||||
- `delete, remove, rm <name>` — `--confirm/-y`.
|
||||
|
||||
### `tea actions variables` (aliases: `variable`, `vars`, `var`)
|
||||
- `list, ls` — `--name` to fetch a single variable.
|
||||
- `set, create, update <name> [value]` — `--file`, `--stdin`.
|
||||
- `delete, remove, rm <name>` — `--confirm/-y`.
|
||||
|
||||
### `tea actions runs` (alias: `run`)
|
||||
- `list, ls` — `--status {success|failure|pending|queued|in_progress|skipped|canceled}`, `--branch`, `--event`, `--actor`, `--since`, `--until`.
|
||||
- `view, show, get <run-id>` — `--jobs` prints the jobs table.
|
||||
- `delete, remove, rm, cancel <run-id>` — `--confirm/-y`.
|
||||
- `logs, log <run-id>` — `--job <id>`, `--follow/-f` (requires the job to be in progress).
|
||||
|
||||
### `tea actions workflows` (alias: `workflow`)
|
||||
- `list, ls`
|
||||
- `view, show, get <workflow-id>`
|
||||
- `dispatch, trigger, run <workflow-id>` — `--ref/-r`, `--input/-i key=value` (repeatable), `--follow/-f`.
|
||||
- `enable <workflow-id>`
|
||||
- `disable <workflow-id>` — `--confirm/-y`.
|
||||
|
||||
## `tea webhooks` (aliases: `webhook`, `hooks`, `hook`)
|
||||
Scope is selected by flag: `--repo`, `--org`, `--global`.
|
||||
|
||||
- `list, ls`
|
||||
- `create, c <webhook-url>` — `--type {gitea|gogs|slack|discord|dingtalk|telegram|msteams|feishu|wechatwork|packagist}` (default: gitea), `--secret`, `--events` (default: push), `--active`, `--branch-filter`, `--authorization-header`.
|
||||
- `update, edit, u <id>` — `--url`, `--secret`, `--events`, `--active` / `--inactive`, `--branch-filter`, `--authorization-header`.
|
||||
- `delete, rm <id>` — `--confirm/-y`.
|
||||
|
||||
## `tea comment, c <issue/pr index> [body]`
|
||||
Add a comment to an issue or PR. Body may be passed as an argument or supplied interactively.
|
||||
@@ -1,30 +0,0 @@
|
||||
# tea CLI — HELPERS
|
||||
|
||||
See [`./index.md`](./index.md) for global options and common flags shared by all commands.
|
||||
|
||||
## `tea open, o`
|
||||
Open the current repository/context in a web browser.
|
||||
|
||||
## `tea notifications` (aliases: `notification`, `n`)
|
||||
Defaults to the current repo; `--mine/-m` aggregates across all your repos. Fields: `id,status,updated,index,type,state,title,repository`. Filters: `--types/-t {issue|pull|repository|commit}`, `--states/-s {pinned|unread|read}` (default: `unread,pinned`).
|
||||
|
||||
- `ls, list`
|
||||
- `read, r [all | <id>]`
|
||||
- `unread, u [all | <id>]`
|
||||
- `pin, p [all | <id>]`
|
||||
- `unpin [all | <id>]`
|
||||
|
||||
## `tea clone, C <repo-slug> [target-dir]`
|
||||
Clone without requiring a local git install. Accepts slug forms: `gitea/tea`, `tea`, `gitea.com/gitea/tea`, `git@gitea.com:gitea/tea`, `https://gitea.com/gitea/tea`, `ssh://gitea.com:22/gitea/tea`. A host in the slug overrides `--login`. Options: `--depth/-d`, `--login/-l`.
|
||||
|
||||
## `tea api <endpoint>`
|
||||
Authenticated HTTP request to the Gitea API. Endpoints are auto-prefixed with `/api/v1/` unless they start with `/api/` or `http(s)://`. Placeholders `{owner}` and `{repo}` are filled from the repo context.
|
||||
|
||||
- `--method/-X {GET|POST|PUT|PATCH|DELETE}` (default GET; switches to POST automatically when a body is provided)
|
||||
- `--field/-f key=value` — string field on body (repeatable).
|
||||
- `--Field/-F key=value` — typed field (numbers, booleans, null, JSON arrays/objects); `@file` or `@-` (stdin); `"null"` forces literal string.
|
||||
- `--data/-d` — raw JSON body (`@file` / `@-`). Incompatible with `-f`/`-F`.
|
||||
- `--header/-H key:value` (repeatable)
|
||||
- `--include/-i` — write status + response headers to stderr.
|
||||
- `--output/-o <file>` — write response body to file (`-` = stdout). **Not the entity commands' format flag**: `-o json` here creates a file named `json` and prints nothing. The body is already JSON.
|
||||
- Quote the endpoint if it contains `?` or `&` to prevent shell expansion.
|
||||
@@ -1,36 +0,0 @@
|
||||
# tea CLI — Index
|
||||
|
||||
Version: `tea 0.14.1` (go-sdk v0.25.1). Source: recursive `--help` traversal. Upstream: https://gitea.com/gitea/tea
|
||||
|
||||
`tea` is a productivity helper for Gitea. It uses the current git repository context (`$PWD`) — owner/repo/login are auto-detected when inside a repo. Config is persisted in `$XDG_CONFIG_HOME/tea`.
|
||||
|
||||
## Global options
|
||||
|
||||
- `--debug, --vvv` — enable debug mode
|
||||
- `--help, -h`, `--version, -v`
|
||||
|
||||
## Common flags (present on nearly every command)
|
||||
|
||||
| Flag | Purpose |
|
||||
|---|---|
|
||||
| `--login, -l <name>` | use a specific login from the config |
|
||||
| `--repo, -r <owner/repo>` | override repository context (local path or slug). **A slug only works where the command is pure API.** `pulls create`, `pulls checkout` and `pulls clean` need a real checkout and read this flag as a path — see [SKILL.md](../../SKILL.md) |
|
||||
| `--remote, -R <name>` | discover login from this git remote |
|
||||
| `--output, -o <fmt>` | output format: `simple, table, csv, tsv, yaml, json`. **Entity commands only** — on `tea api` the same flag is a FILE NAME, see [HELPERS](./helpers.md) |
|
||||
| `--page, -p <n>` / `--limit, --lm <n>` | pagination (defaults 1 / 30) |
|
||||
| `--fields, -f <list>` | which columns to print |
|
||||
|
||||
## Command categories
|
||||
|
||||
```
|
||||
ENTITIES: issues, pulls, labels, milestones, releases, times,
|
||||
organizations, repos, branches, actions, webhooks, comment
|
||||
HELPERS: open, notifications, clone, api
|
||||
MISC: whoami, admin
|
||||
SETUP: logins, logout, ssh-keys
|
||||
```
|
||||
|
||||
- [ENTITIES](./entities.md)
|
||||
- [HELPERS](./helpers.md)
|
||||
- [MISC](./misc.md)
|
||||
- [SETUP](./setup.md)
|
||||
@@ -1,17 +0,0 @@
|
||||
# tea CLI — MISCELLANEOUS
|
||||
|
||||
See [`./index.md`](./index.md) for global options and common flags shared by all commands.
|
||||
|
||||
## `tea whoami`
|
||||
Show the currently logged in user.
|
||||
|
||||
## `tea admin, a`
|
||||
Operations requiring admin access on the Gitea instance.
|
||||
|
||||
### `tea admin users` (alias: `u`)
|
||||
Fields: `id,login,full_name,email,avatar_url,language,is_admin,restricted,prohibit_login,location,website,description,visibility,activated,lastlogin_at,created_at`.
|
||||
|
||||
- `list, ls`
|
||||
- `create, add, new` — `--username/-u`, `--password/-p` / `--password-file` / `--password-stdin`, `--email/-e`, `--full-name`, `--admin`, `--restricted`, `--prohibit-login`, `--no-must-change-password`, `--visibility {public|limited|private}`.
|
||||
- `edit, update, e, u <username>` — paired flags: `--password` (or `--password-file`/`--password-stdin`), `--email/-e`, `--full-name`, `--description`, `--website`, `--location`, `--admin`/`--no-admin`, `--restricted`/`--no-restricted`, `--prohibit-login`/`--allow-login`, `--active`/`--inactive`, `--no-must-change-password`, `--visibility`, `--max-repo-creation` (-1 = unlimited), `--allow-git-hook`/`--no-allow-git-hook`, `--allow-import-local`/`--no-allow-import-local`, `--allow-create-organization`/`--no-allow-create-organization`.
|
||||
- `delete, rm, remove <username>` — `--confirm/-y`.
|
||||
@@ -1,19 +0,0 @@
|
||||
# tea CLI — SETUP
|
||||
|
||||
See [`./index.md`](./index.md) for global options and common flags shared by all commands.
|
||||
|
||||
## `tea logins` (alias: `login`)
|
||||
- `list, ls`
|
||||
- `add` — interactive when called without args. `--name/-n`, `--url/-u` (`$GITEA_SERVER_URL`), `--token/-t` (`$GITEA_SERVER_TOKEN`), `--user` (`$GITEA_SERVER_USER`), `--password/--pwd` (`$GITEA_SERVER_PASSWORD`), `--otp` (`$GITEA_SERVER_OTP`), `--scopes` (`$GITEA_SCOPES`), `--ssh-key/-s`, `--ssh-agent-principal/-c`, `--ssh-agent-key/-a`, `--insecure/-i`, `--no-version-check/--nv`, `--helper/-j`, `--oauth/-o` (plus `--client-id`, `--redirect-url`).
|
||||
- `edit, e` — interactive.
|
||||
- `delete, rm <name>`
|
||||
- `default [<login>]` — get or set the default login.
|
||||
- `oauth-refresh [<login>]` — refresh an OAuth token (opens browser if the refresh token is also expired).
|
||||
|
||||
## `tea logout <name>`
|
||||
Remove a stored login.
|
||||
|
||||
## `tea ssh-keys` (alias: `ssh-key`)
|
||||
- `list, ls`
|
||||
- `add <key-file>` — `--title/-t` (defaults to filename without extension).
|
||||
- `delete, rm <key-id>` — `--confirm/-y`.
|
||||
Reference in New Issue
Block a user