Files
marketplace/plugins/kettle/AGENTS.md
T
naudachu 01fb5a2703 feat: publish releases with this repository's own SDK code
There is no CI: the instance has no act_runner and none is planned, so releases
are cut by hand. That makes `make check` the only thing standing between a
mistake and the tracker, and it is one command: gofmt, vet, the suite with the
cache defeated, `go mod verify`, a vendored build, and `kettle gen skills
--check`. The last one is the invariant worth having — the plugin's SKILL.md
command reference is generated from the binary's registry, so a flag that
changed cannot ship with documentation that recommends the old one.

`cli/cmd/release` publishes to Gitea using the same SDK the binary already
vendors, which is a pleasing thing to be able to say: nothing third-party
handles the artifacts. It is a second binary rather than a `kettle` subcommand
on purpose — `kettle`'s command tree is what generates the plugin's skills, so a
verb there ships to every operator, and publishing a release is build
infrastructure. It is idempotent end to end: an existing release for the tag is
reused, an asset of the same name is replaced rather than doubled, and a retried
run converges instead of duplicating.

`make release` refuses three things, each with its own message: a dirty working
tree, a TAG that is not what `git describe` reports, and a tag the remote does
not have. A release built from uncommitted code is unreproducible and nobody
finds out until they need to reproduce it.

`kettle version` reports the stamp, the toolchain and the VCS revision. The
default is `dev`, and a hand build says so and means it — a binary out of
somebody's working tree is not a release and must not claim to be one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 01:01:27 +05:00

177 lines
9.2 KiB
Markdown

# AGENTS.md — the kettle plugin
This plugin is a **thin wrapper over the `kettle` binary**, which lives in this
same repository at [`cli/`](../../cli) and is documented in
[`cli/AGENTS.md`](../../cli/AGENTS.md). The binary owns everything mechanical:
what an issue is, where the store lives, who this machine is, and how issues move
to and from Gitea. Its layering, its walk, its round-trip guarantees and its
tests are described there and are **not repeated here** — one design, one place.
What a plugin can carry that a binary cannot is the reason this directory still
exists:
1. **The rules an operator states.** A binary can refuse to evict an
`origin: local` issue; it cannot refuse to be run in the wrong directory, or
decide that a migration clash is not a model's to resolve.
2. **Routing.** A `description:` in a SKILL.md frontmatter is the only thing that
decides whether an agent loads a skill at all, and no generator can write it.
3. **Procedures.** How to turn a one-line request into a properly filled
template, what to ask the user and what to never invent.
## Installing the binary
**`kettle` is not on anybody's PATH by default.** Nothing in this plugin ships
it, and a skill that assumes it exists fails with `command not found: kettle`
which is the clearest failure available, and every skill says what to do about
it rather than falling back to something else.
```bash
cd cli && go build -o ~/.local/bin/kettle ./cmd/kettle # from this repository
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).
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
that explains where a run resolved to once it does exist.
## The generated command reference
Every command line in `skills/{project,issue,sync}/SKILL.md` between
```
<!-- kettle:gen --> … <!-- /kettle:gen -->
```
is written by `kettle gen skills` from the command registry the binary is built
from, and **must not be edited by hand** — the next run replaces it. Everything
outside the markers is prose and comes back byte for byte, which is why the
frontmatter is safe.
```bash
cd cli && go build -o /tmp/kettle ./cmd/kettle
/tmp/kettle gen skills --out plugins/kettle/skills # rewrite the blocks
/tmp/kettle gen skills --out plugins/kettle/skills --check # exit 1 if stale
```
`--check` is what a pre-commit hook or a CI step calls; it writes nothing. A file
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
`<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 |
| `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
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
the generator, not to paste a block that will rot.
## Layout
```
.claude-plugin/plugin.json the manifest (the marketplace catalog is one level
up, in the repo root's .claude-plugin/)
agents/
kettle-runner.md subagent (Haiku): runs kettle commands, reports a
receipt. Batches only, and no opinions about content
hooks/
hooks.json registers the one hook: PreToolUse(Bash)
agents-sync.sh keeps every directory canonical: AGENTS.md a real
file, CLAUDE.md a symlink to it
skills/
init/ SKILL.md /kettle:init — operator-only; the rules around
`kettle init`
auth/ SKILL.md /kettle:auth — the credential workflow around
`kettle auth` and `kettle init --login`
project/ SKILL.md generated flag reference: init, auth, config, gen
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
```
`references/format.md` is the one document here that the binary does not
generate and does not own a copy of. It is the format's statement of intent —
identity, metadata, label namespaces, the per-type templates, the language rule —
and it stays hand-written.
## The rules that must survive, because a binary cannot state them
- **An `origin: local` issue is the only copy of that work.** A push deletes the
local file only after the tracker confirms the write and the number → slug
ledger is written; nothing else deletes it, ever.
- **A closed issue is evicted, not archived** — and a local one is never evicted,
in any state, not even when it is named on the command line.
- **Body prose is Russian; the title and the section headers are English.**
- **Do not run `init` inside a linked worktree.** `.kettle/` is gitignored, a
worktree reaches the main checkout's store on its own, and a marker there gives
one project two stores — the second of which disappears with the branch.
- **Which directory is the project is a statement a person makes.** `/kettle:init`
keeps `disable-model-invocation: true` for that reason, and never invents an
`--at`.
- **A migration clash is the operator's to resolve.** The binary stops and names
both files; one of them may be somebody's only copy.
## What this plugin no longer ships, and why
| gone | replaced by |
|---|---|
| every Python script under `skills/*/scripts/` — the whole domain, bridge and transport | the `kettle` binary; see `cli/AGENTS.md` for the three runtime failures that motivated it |
| `tests/` — the stdlib `unittest` suite | `cd cli && go test ./...` |
| 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 |
`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
`CLAUDE.md` a symlink to it — and the convention itself is documented once,
in [the repository root's AGENTS.md](../../AGENTS.md#the-agentsmd-convention). It
cannot fail a tool call: it exits 0 on every path, including its own bugs —
documentation maintenance is not permitted to break a build.
All of it is in git history. `git log --diff-filter=D` finds it if a decision
needs to be re-read rather than re-derived.
## Tests
The plugin has no test suite of its own; the binary's is the suite.
```bash
cd cli && go test ./...
/tmp/kettle gen skills --out plugins/kettle/skills --check
```
The second line is the plugin's only mechanical invariant: the documentation an
agent reads agrees with the binary it is documenting.
## Keeping this file true
- **Scope:** everything under `plugins/kettle/` — the skills, the runner subagent,
the two hooks, and the group ⇄ skill seam. `skills/` and `agents/` carry no
AGENTS.md of their own on purpose: a SKILL.md already states its own procedure,
and a second document beside it would be the copy that goes stale.
- **Update it when** a skill is added or removed (the layout and the generated-region
table both name every one), a hook is added or its event changes, a rule in *the
rules that must survive* changes, or the binary takes over something this plugin
used to state.
- **Do not** restate the binary's design. [`cli/AGENTS.md`](../../cli/AGENTS.md) and
the eight package files under it own that; if the two ever disagree, the binary is
right and the prose is stale.