BREAKING: the plugin is `kettle`, not `tea`, and its commands are `/kettle:*`. It also now needs a binary on PATH that it did not need before; the README and every skill say how to get one and what a missing one looks like. The plugin was 3800 lines of Python doing what a compiled binary does better, and the name pointed at a tool that no longer takes part: `tea` is Gitea's CLI, and since the transport moved into the binary nothing here shells out to it for issues at all. A plugin named after it was going to keep suggesting otherwise. Deleted: 19 scripts, the 14-file unittest suite, and the tea-guard hook. The guard blocked any `tea` invocation that would run under a login the model picked instead of the operator; the binary holds its own credentials and reads the pinned login out of the project's own config, so that failure is no longer expressible and there is nothing left to police. agents-sync stays — it is about AGENTS.md symlinks and has nothing to do with any of this. What the plugin keeps is what only a plugin can carry: the rules an operator states and a binary cannot enforce. `init` still refuses to run inside a linked worktree and still may not be model-invoked, because which directory is the project is a statement a person makes. The issue format reference stays here and stays the source of truth. The runner subagent is still for batches and still may not decide what an issue says. The command reference in the issue, sync and project skills is GENERATED from the binary's own command registry, between markers, so a flag that changed cannot ship with a skill that recommends the old one. `kettle gen skills --check` exits non-zero when they drift. The generator owns the region and nothing outside it: the frontmatter description, which is what decides whether a skill loads at all, stays hand-written. `use` survives and is the one place `tea` is still named — for releases, webhooks and actions, which kettle does not cover. Its instruction to write `--login "$GITEA_LOGIN"` and let the hook substitute the pin was true until this commit and is now rewritten: `tea` keeps its own configuration, kettle keeps its own, and configuring one configures nothing in the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.9 KiB
name, description
| name | description |
|---|---|
| project | Generated flag reference for the project-level `kettle` commands — `kettle init`, `kettle auth`, `kettle config`, `kettle gen`. Load it to look up the exact flags and defaults of one of those four, or when a command answers "no project" / "no login" and you need `kettle config` to say what this directory resolved to. The rules around initializing are /kettle:init and the credential workflow is /kettle:auth; this file is the flag table both of them point at. |
kettle project — the project itself
kettle resolves everything from one marker. <project>/.kettle/ is created by
kettle init and never inferred: .git is in every clone, so a tool that
guessed a root from one would write issues into whatever tree it happened to be
standing in. With no marker anywhere the command stops and names the directories
it searched — that is an answer, not a fallback.
Two configuration files, and the split is the point. <project>/.kettle/config.yaml
holds the tracker repository and the name of a login; the name is worth
nothing on its own, which is what makes it safe inside a working tree.
~/.config/kettle/logins.yaml (0600, one per machine, $KETTLE_CONFIG_HOME or
$XDG_CONFIG_HOME move it) holds the tokens. KETTLE_LOGIN, KETTLE_REPO,
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
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.
kettle gen is a maintainer command: it rewrites the generated region of these
SKILL.md files from the command registry the binary was built from. Run it after
changing the CLI, never to "fix" documentation by hand.
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 auth list | add | remove <name>
manage the tokens this machine holds
Credentials live in one file per machine, outside every working tree, mode 0600. A project pins a login by NAME; the name is worth nothing on its own, which is what makes it safe to keep in a file inside the repository.
The token is read from standard input unless --token is given, because an argument is in the shell history the moment it is typed:
kettle auth add --name noodles --url https://git.example.com < token.txt
pass show gitea/token | kettle auth add --name noodles --url https://git.example.com
list never prints a token. There is no flag to make it.
| flag | default | what it does |
|---|---|---|
--name |
— | login name (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) |
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 remove noodles # forget it
kettle config
show what this project resolved to
Every path and every setting, with the overrides already applied, so a run that went somewhere unexpected can be explained without guessing.
The token is never printed — only whether one was found.
This is the command to reach for when the store looks empty, when a push says 401, or when two directories disagree about which project they are in.
kettle config # resolved paths and settings
kettle gen skills
write the plugin's SKILL.md files from the command registry
A SKILL.md tells an agent how to invoke this binary. Hand-written, it drifts: a flag is renamed here and the documentation goes on recommending the old one, and the agent that reads it fails in a way nobody traces back to a stale sentence. Everything those files say about a command — its usage line, its flags with their defaults, its worked examples — is already in the registry this binary is built from, so it is written from there and cannot disagree.
THE GENERATOR OWNS A REGION, NOT A FILE. Each SKILL.md carries a pair of HTML
comment markers — kettle:gen to open and /kettle:gen to close, both written in
the <!-- … --> form and visible at the top and bottom of the block below.
Everything between them is replaced on every run; every byte outside them comes
back exactly as it was, which matters most for description:, the prose that
decides whether an agent loads the skill at all, and the one thing here that no
generator can write.
A file with no markers is REPORTED AND LEFT ALONE, never overwritten: clobbering somebody's prose because they forgot a marker is the failure this design exists to prevent. A file that does not exist yet is created with a frontmatter stub around a generated block, for a human to fill in.
The output is deterministic to the byte — no timestamps, no map iteration — so regenerating something that has not changed produces no diff. --check is that property made useful: it writes nothing and exits 1 when any file on disk differs from what would be generated, which is what a pre-commit hook or a CI step calls. It wins over --dry-run when both are given.
| flag | default | what it does |
|---|---|---|
--check |
false |
write nothing, exit 1 if anything is out of date |
--dry-run |
false |
print what would change; write nothing |
--out |
— | directory the skills live in; one /SKILL.md under it |
kettle gen skills --out ../plugins/kettle/skills # write the region in every group's SKILL.md
kettle gen skills --out ../plugins/kettle/skills --dry-run # print what would change; write nothing
kettle gen skills --out ../plugins/kettle/skills --check # exit 1 if the docs are out of date
kettle init
make this directory a project that tracks issues
Creates .kettle/ — the marker every other command resolves the store from,
and .kettle/config.yaml, which says which tracker repository these issues
belong to and which login to reach it under.
The marker is deliberately something an operator makes, not something inferred
from the tree: .git is in every clone, so anything that inferred a root from
one would write issues into whatever it happened to be installed in.
--login pins a name, never a credential. The tokens live in one file per
machine, outside every working tree, managed with kettle auth.
All of it is idempotent: it creates .kettle/issues and .kettle/payload, migrates an older store in if it finds one (either layout the tea plugin used, oldest first), writes the config without disturbing settings it was not given, and adds .kettle/ to .gitignore. Each migration is a move, not a copy — two stores is the state the marker exists to prevent — and it refuses to pick a winner when both sides hold a file of the same name.
Do NOT run this inside a linked worktree. A worktree is the same project on another branch and reaches the store by a hop out to the main checkout; a marker here would give one project two stores, and the directory holding the second one disappears with the branch.
| flag | default | what it does |
|---|---|---|
--at |
— | directory to initialize (default: the working directory) |
--dry-run |
false |
report what would happen; change nothing |
--login |
— | name of a login in the machine-wide file (see kettle auth) |
--repo |
— | tracker repository, as owner/name |
kettle init # initialize the current directory
kettle init --login noodles --repo claude-skills/marketplace # and point it at a tracker
kettle init --at ~/code/x # initialize somewhere else
kettle init --dry-run # say what it would do, touch nothing