01fb5a2703
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>
177 lines
9.2 KiB
Markdown
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.
|