Files
marketplace/AGENTS.md
T
naudachu 8b1b11001a feat: drop the kettle plugin; the binary writes its own skills
The plugin and the binary shipped on two release cadences and nothing on an
operator's machine ever checked that the one they installed described the other.
The generated flag block existed precisely so a renamed flag could not ship with
documentation recommending the old one — and then shipped one version behind the
registry it came from, which is the same bug one hop downstream.

So the prose moved into the binary. `internal/scaffold` embeds every document;
`kettle init` and `kettle gen scaffold` write them into a project's own
`.claude/`. The two cannot disagree because there is one artefact.

The namespace survived the move. A project's skills are flat, so the prefix is
spelled into the directory name (`kettle-issue`); a project's *commands* take
their namespace from a subdirectory, so `commands/kettle/init.md` is still
`/kettle:init`. Four of the six command files are thin pointers at a skill, and
that is what kept ~1,600 lines of `/kettle:…` cross-references true without a
rewrite. `init` and `auth` lost `disable-model-invocation: true` — being a
command is that property — and `auth` now restricts `allowed-tools` so a model
cannot reach `kettle auth add` at all.

`gen scaffold` writes files whole rather than splicing a region. The old
refusal protected somebody's hand-written prose around the block; that prose is
embedded now, so there is none to protect, and preserving local edits would
freeze a project's documentation at whatever version first initialized it.
`--check` warns before an upgrade discards one.

The plugin's `agents-sync.sh` — 141 lines of Python behind a filename that said
`.sh` — became `internal/mirror` and `kettle mirror`. Same seven branches, same
refusal to merge two real files that differ, now with a table test per branch
and a check that a repair converges in one pass. `--hook` is the PreToolUse
form and exits 0 on every path including a panic. It is opt-in per project,
which is strictly narrower than the plugin hook that was on for everybody who
installed it.

`kettle init --interactive` walks a person through the login, the token (read
with the echo off, so it lands in no history and no file), the repository, the
`.claude/` tree and the mirror hook. It refuses a stdin that is not a terminal
and names the flags instead: every question it asks has one, and it performs
nothing itself, so an interactive run and a flag run are one code path.

Two rules that used to be prose are now the binary's: init refuses a linked
worktree and names the main checkout, and writing into an existing
`.claude/settings.json` is refused with the snippet printed rather than
reformatting a file the operator commits.

The scaffold version stamp went to its own `.kettle/scaffold.yaml` rather than
into `config.yaml`, because unknown keys there are a hard error and that file
may be committed and read by whatever build each machine has.

golang.org/x/term becomes a direct dependency; it was already in the tree
indirectly, so no module was added.

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

6.3 KiB

AGENTS.md — the repository root

One repository holding the kettle binary and a Claude Code plugin marketplace with one plugin left in it. Three things live here and nothing else does:

cli/                              the kettle binary — Go, no cobra, 9 packages
plugins/                          one directory per plugin
.claude-plugin/marketplace.json   the catalog: one entry per plugin

Where to go from here, and each of these directories documents itself:

directory what it is
cli/ one Go module, two binaries: kettle, which owns this project's connection to its tracker — the format, the store, the credentials, the transport, through kettle api every Gitea entity that has no command of its own, and the skills and commands it writes into a project's .claude/ — and cmd/release, which publishes this repository's own releases. make check is the gate; there is no CI here
plugins/ what a plugin is here, and what the catalog entry has to match
plugins/tdl/ the Three Dots Labs Go rule set — no binary, no state, just rules and templates

The one architectural fact worth carrying

A binary holds what can be enforced; prose holds what can only be stated. Anything mechanical belongs in Go where a test can hold it down; anything that is a judgement an operator makes belongs in a SKILL.md.

There used to be a second half to that sentence — and a plugin holds the prose — and it is gone, because it was wrong in a way that took a year to show. kettle shipped as a plugin wrapping a binary, on two release cadences, and nothing on an operator's machine ever checked that the plugin they installed described the binary they installed. The generated flag block existed precisely so a renamed flag could not ship with documentation recommending the old one, and it shipped one version behind the registry it came from — the same bug, one hop downstream.

So the prose moved into the binary. cli/internal/scaffold embeds it, kettle init and kettle gen scaffold write it into a project's own .claude/, and the two cannot disagree because there is only one artefact. What a project gets:

.claude/commands/kettle/*.md      /kettle:init, /kettle:auth, /kettle:issue, …
.claude/skills/kettle-*/          loaded by description, never by name
.claude/agents/kettle-runner.md   the batch runner, on a cheap model

The same rule made kettle api (one door for every request, rather than requiring tea and its second set of invisible logins) and kettle mirror (below). Each time, something mechanical was sitting where no test could reach it, and each time it came into Go.

The AGENTS.md convention

Every directory with a story documents itself, in that directory. This file is a map, not a manual — it says what lives where and sends you down. A reader who opens cli/internal/gitea/ gets the transport's rules from cli/internal/gitea/AGENTS.md and does not have to load the whole repository's design to change one request.

Three rules make that work:

  1. AGENTS.md is the real file; CLAUDE.md beside it is a symlink to it. kettle mirror enforces that and repairs any directory that drifted — it renames, re-points and swaps, and it never deletes content. Two real files with different content is the one case it refuses to resolve and reports instead. CLAUDE.md is gitignored, because it is generated.
  2. A directory's file describes that directory only. What a parent or a child owns gets a link, never a second copy — the copy is what goes stale. If a sentence is true of the whole binary it belongs in cli/AGENTS.md; if it is true of one package it belongs in that package's file.
  3. Every file ends with its own maintenance contract — the Keeping this file true section. It names the files the document covers and what kind of change obliges an edit.

What keeps them true

Nothing automatic, and that is a choice. Keeping these files honest is the job of whoever changes the code they describe, which is what the Keeping this file true section at the bottom of each one is for.

A hook that nagged after every write was written and then removed: it would have fired for every user of the kettle plugin, on every edit in every repository they touched, to enforce a documentation convention that is this repository's and nobody else's. That argument survived the plugin. kettle mirror --hook is what stayed, and it is narrower on both axes: it repairs the filesystem layout rather than asking anybody for anything, and it is opt-in per projectkettle init --mirror-hook, or one question in kettle init --interactive. It cannot fail a tool call; it exits 0 on every path, including its own bugs, because documentation maintenance is not permitted to break a build.

This repository registers it in .claude/settings.json, which is the one thing under .claude/ that is not gitignored here — a fresh clone should get the convention without anyone remembering to wire it up.

Development

cd cli && make check          # fmt, vet, test, go mod verify, build, docs — the gate
cd cli && make help           # install, dist, release

There is no CI on the instance this lives on, so make check is the only thing between a mistake and the tracker, and it is on whoever is committing to run it. Its last step is the repository's one mechanical documentation invariant, and it is circular on purpose: the documents an operator will be given are embedded in the binary with the registry's flag tables already spliced in, so a flag change makes them stale and gen scaffold --check exits 1. Regenerate, then rebuild — the binary has to pick the new bytes up.

Everything else in this tree — including every AGENTS.md — is prose, held true by kettle mirror and by whoever is editing.

Keeping this file true

  • Scope: the repository layout, the binary/prose split, and the AGENTS.md convention itself. Every deeper subject belongs to a deeper file.
  • Update it when a top-level directory appears or goes, a plugin is added or removed, the marketplace catalog changes shape, or the mirror hook's behaviour changes.
  • Do not put a command reference, a package's rules, or a skill's procedure here. Link to the file that owns it.