Files
marketplace/cli/cmd/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

1.9 KiB

AGENTS.md — cmd/kettle

The binary's entry point, and all of it:

func main() { os.Exit(cmd.Main(os.Args[1:])) }

One file, main.go, holding a package comment and that line. The sibling cmd/release is the module's other binary — build infrastructure, deliberately not a kettle verb.

Why it is empty

Everything a main usually accumulates — flag parsing, dispatch, usage text, error formatting, exit codes — is in internal/cmd, where it is testable. A main package cannot be imported, so anything written here can only be exercised by running the binary; the command tree is instead a library with one caller, and its tests run it as a subprocess and call into it directly where that is cheaper.

The exit status is the only thing this layer owns, and it owns it because os.Exit skips deferred functions: it has to happen after everything else is finished, at the outermost frame, and nowhere else in the tree may call it.

The version a build reports is not stamped here either. -ldflags -X names internal/cmd.Version, because that is where the version command reads it and where a test can build with the flag and read the answer back — a -X whose symbol path is one character wrong is silently ignored, and the binary goes on saying dev.

Adding a command

Nothing here changes. A new command is a register(&Command{…}) in an init() over in internal/cmd — that is the whole point of a registry.

Keeping this file true

  • Scope: main.go, and the reason it stays this short.
  • Update it when this package grows a second file or a line that does anything but delegate — which should be treated as a design change and argued for, not documented after the fact.
  • Do not describe commands, flags or exit codes here.