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>
This commit is contained in:
naudachu
2026-08-12 16:17:24 +05:00
parent f18a633185
commit 8b1b11001a
445 changed files with 231172 additions and 1339 deletions
+67 -50
View File
@@ -1,51 +1,63 @@
# claude-skills — a Claude Code plugin marketplace
# claude-skills — the `kettle` binary, and a plugin marketplace
One repository, one marketplace, two plugins, and the `kettle` binary the issue
plugin is built on. Register the marketplace once and install whichever pieces you
want; each plugin is independent and carries its own manifest and docs.
One repository holding two things: **`kettle`**, a Go binary that keeps a
project's issues as local markdown and syncs them with Gitea, and a small Claude
Code **plugin marketplace** with one plugin in it.
## Installation
## Installing kettle
`kettle` is one binary and it installs itself into your project. There is no
plugin to add — `cli/go.mod` requires **Go 1.26**, and the first build downloads
eight modules, verified against `go.sum`:
```bash
go install git.noodles.cam/claude-skills/marketplace/cli/cmd/kettle@latest
# or from a clone of this repository, version stamped:
cd cli && make install # ~/.local/bin/kettle
cd cli && make install BINDIR=$(go env GOPATH)/bin
```
Put the target directory on your `PATH` and check with `kettle version`. Then, in
a project you want to track issues in:
```bash
kettle init --interactive
```
That walks you through the login, the token (typed with the echo off, so it lands
in no history and no file), the tracker repository, and the `.claude/` tree —
which is where the slash commands and skills come from:
```
.claude/commands/kettle/*.md /kettle:init /kettle:auth /kettle:issue
/kettle:sync /kettle:api /kettle:project
.claude/skills/kettle-*/ loaded by description, never by name
.claude/agents/kettle-runner.md the batch runner, on a cheap model
```
Every flag table in those documents is rendered from the binary's own command
registry, and the prose around it is embedded in the same binary. **They cannot
describe a version of `kettle` other than the one that wrote them** — which is
the whole reason this stopped being a plugin. `kettle config` tells you when the
tree on disk was written by an older build; `kettle gen scaffold` refreshes it.
Prefer flags to prompts? Everything `--interactive` asks has one:
```bash
kettle init --login noodles --repo owner/name --mirror-hook
kettle init --no-scaffold # marker and config only
```
## The marketplace
```
/plugin marketplace add https://git.noodles.cam/claude-skills/marketplace.git
```
Working from a local clone? Point at the directory instead:
```
/plugin marketplace add /path/to/marketplace
```
Then install what you need:
```
/plugin install kettle@claude-skills
/plugin install tdl@claude-skills
```
Use `/plugin` to enable, disable, or update them later.
**`kettle` also needs its binary**, which no plugin can install for you. Build it
from this repository — `cli/go.mod` requires **Go 1.26**, and the first build
downloads eight modules, verified against `go.sum`:
```bash
cd cli && make install # ~/.local/bin/kettle, version stamped
cd cli && make install BINDIR=$(go env GOPATH)/bin
# or plain go, without the version stamp:
cd cli && go build -o ~/.local/bin/kettle ./cmd/kettle
go install git.noodles.cam/claude-skills/marketplace/cli/cmd/kettle@latest
```
Put the target directory on your `PATH` and check with `kettle version`. A skill
that answers `command not found: kettle` is telling you exactly this.
## What ships here
| Plugin | Commands | What it does |
|---|---|---|
| [`kettle`](plugins/kettle) | `/kettle:init` `/kettle:auth` `/kettle:issue` `/kettle:sync` `/kettle:api` `/kettle:project` | Issues as local markdown, cleanly layered. Issues are units of work offline first and tracker rows second; the `kettle` binary does the work, and each skill's command reference is generated from the binary's own command registry so it cannot drift |
| [`tdl`](plugins/tdl) | `/tdl:audit` | Three Dots Labs Go conventions as an enforceable rule set — audits a Go project against nine categories of CQRS/DDD/Clean-Architecture rules by severity, or scaffolds services, handlers, entities, repositories and Watermill adapters from templates that already follow them |
---
@@ -68,7 +80,8 @@ internal packages with one direction of imports — see
| `kettle init` | makes this directory a project: creates the `.kettle/` marker every other command resolves the store from, migrates an older store in, gitignores it |
| `kettle auth` | manages the tokens this machine holds — one file, mode 0600, outside every working tree |
| `kettle config` | prints every path and setting a run resolved to, with overrides applied. Never prints a token |
| `kettle gen skills` | rewrites the plugin's SKILL.md command reference from the binary's own command registry |
| `kettle gen scaffold` | writes the `.claude/` commands, skills and subagent, prose and flag tables both, from inside this binary |
| `kettle mirror` | keeps `CLAUDE.md` a symlink to `AGENTS.md` in every directory below here. `--hook` is the `PreToolUse` form and never fails a call |
| `kettle version` | what this binary was built as — `dev` from a working tree, the tag when a release build stamped it in |
**issue** — units of work, offline, no tracker involved
@@ -239,22 +252,25 @@ Five steps in one command, non-zero the moment any of them fails:
| `go vet ./...` | the compiler's own second opinion |
| `go test -count=1 ./...` | the whole suite, with the test cache defeated: a gate that can pass because it passed an hour ago on different code is not a gate |
| `go mod verify` + `go build -mod=vendor ./...` | every module matches its hash in `go.sum`, and the committed `vendor/` is complete and is what compiles. A `vendor/` that has drifted from `go.mod` breaks nobody until somebody builds with a cold module cache |
| `kettle gen skills --check` | the plugin's SKILL.md command reference still agrees with the binary it documents. Exit 1 the moment it does not, and `gen skills` without `--check` is the fix |
| `kettle gen scaffold --check` | the documents embedded in the binary still agree with the registry it is built from. Exit 1 the moment they do not, and `dist/kettle gen scaffold --out internal/scaffold/assets` is the fix |
That last one is the invariant that keeps the plugin honest: everything a
SKILL.md says about a `kettle` command — its usage line, its flags, its
examples — is generated from the registry the binary is built from, between
`<!-- kettle:gen -->` markers. Prose outside the markers is never touched.
That last one is circular on purpose. What a project gets is embedded in the
binary with the registry's flag tables **already spliced in**, so the embedded
copy has to hold what the current registry would produce. Change a flag and it
goes stale; regenerate, then rebuild, because the binary has to pick the new
bytes up.
### Where the design notes are
**Every directory with a story carries its own `AGENTS.md`**, starting at [the
repository root](AGENTS.md) and recursing into [`cli/`](cli/AGENTS.md), each of its
packages, and each plugin. A `CLAUDE.md` beside one is a generated symlink to it, and
two hooks in the `kettle` plugin keep the arrangement honest: one repairs the layout
before every Bash call, the other asks for the nearest `AGENTS.md` to be corrected
after a file is written. Read the one next to the code you are changing rather than
the whole tree.
packages, and each plugin. A `CLAUDE.md` beside one is a generated symlink to it,
and `kettle mirror` keeps that arrangement honest — six of the seven states it can
find are repaired losslessly, and the seventh (two real files whose contents
differ) is reported and left alone, because one of them is somebody's writing.
Register it with `kettle init --mirror-hook` if you want it run before every Bash
call. Read the file next to the code you are changing rather than the whole
tree.
`make help` lists the rest — `build`, `install [BINDIR=…]`, `dist`, `clean`.
`make install` puts a version-stamped binary in `~/.local/bin` (override with
@@ -275,7 +291,8 @@ repository's own code. `cli/cmd/release` is a second binary in the same module
that talks to Gitea's API through the same SDK `kettle` uses: no third-party
action, and nothing between a tag and what people download that is not in this
tree. It is deliberately not a `kettle` subcommand — `kettle` is a tool for
issues, and its command tree is what generates the plugin's documentation.
issues, and its command tree is what generates the documents it writes into a
project.
```bash
cd cli