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>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# AGENTS.md — plugins/
|
||||
|
||||
One directory per plugin, and each is self-contained: **a plugin's root is its
|
||||
directory here**, so `${CLAUDE_PLUGIN_ROOT}` resolves inside it and every path a
|
||||
plugin uses stays relative to itself.
|
||||
|
||||
| plugin | what it is |
|
||||
|---|---|
|
||||
| [`kettle/`](kettle/AGENTS.md) | issues as local markdown, driven by the `kettle` binary in [`cli/`](../cli/AGENTS.md). Skills, a runner subagent, two hooks |
|
||||
| [`tdl/`](tdl/AGENTS.md) | Three Dots Labs Go conventions as an enforceable rule set. One skill, no binary, no state |
|
||||
|
||||
## Adding one
|
||||
|
||||
Two edits, and nothing else in the repository needs to know:
|
||||
|
||||
1. a directory here holding `.claude-plugin/plugin.json`, plus whatever of
|
||||
`skills/`, `agents/`, `hooks/`, `commands/` it needs;
|
||||
2. one entry in [`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json)
|
||||
whose `source` is the path into this directory.
|
||||
|
||||
**The catalog entry's `name` and the directory name must match, and both must match
|
||||
`plugin.json`.** They are three spellings of one identity: the catalog resolves a
|
||||
`source` path, the manifest names the plugin, and the directory is what
|
||||
`${CLAUDE_PLUGIN_ROOT}` becomes. A rename that misses one of the three installs a
|
||||
plugin that cannot find its own files — which is exactly what happened when `tea`
|
||||
became `kettle`.
|
||||
|
||||
The catalog's `description` is what somebody reads in `/plugin`; the manifest's is
|
||||
what the installed plugin reports. Keep them saying the same thing.
|
||||
|
||||
## What a plugin may and may not do
|
||||
|
||||
- **A skill is prose an agent loads on demand.** Its frontmatter `description:` is
|
||||
the only thing that decides whether it gets loaded at all, which makes it the one
|
||||
line worth tuning against real failures — and the one line no generator may write.
|
||||
- **Heavy reference material goes in `references/`**, not in the SKILL.md. The skill
|
||||
says when to read it.
|
||||
- **A hook must fail open.** Both of `kettle`'s exit 0 on every path, including
|
||||
their own bugs: a plugin that breaks a user's Bash call because its documentation
|
||||
helper crashed is worse than no plugin.
|
||||
- **State belongs to the project, never to the plugin directory.** A plugin lives in
|
||||
a versioned cache that changes path on every update; anything written inside it is
|
||||
lost or invisible from the next project. This is not hypothetical — it is the bug
|
||||
that motivated rewriting `kettle`'s Python scripts as a binary, and the whole
|
||||
reason a project carries a `.kettle/` marker.
|
||||
|
||||
## Keeping this file true
|
||||
|
||||
- **Scope:** what a plugin directory is, how the catalog points at it, and the rules
|
||||
common to all of them. The table names every plugin here.
|
||||
- **Update it when** a plugin is added or removed or renamed, or when the catalog
|
||||
gains a field a plugin author has to fill in.
|
||||
- **Do not** describe an individual plugin's skills or design; each has its own file.
|
||||
@@ -31,8 +31,9 @@ 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. `vendor/` is committed, so a build from a clone needs
|
||||
no network.
|
||||
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
|
||||
@@ -89,7 +90,7 @@ agents/
|
||||
kettle-runner.md subagent (Haiku): runs kettle commands, reports a
|
||||
receipt. Batches only, and no opinions about content
|
||||
hooks/
|
||||
hooks.json registers the PreToolUse 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/
|
||||
@@ -138,7 +139,12 @@ and it stays hand-written.
|
||||
| `.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.
|
||||
`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.
|
||||
@@ -154,3 +160,17 @@ cd cli && go test ./...
|
||||
|
||||
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.
|
||||
|
||||
@@ -27,7 +27,7 @@ the tool.
|
||||
- **Claude Code** — CLI, desktop app, or IDE extension.
|
||||
- **The `kettle` binary, on your `PATH`.** It is not installed for you, and
|
||||
nothing here works without it — see below.
|
||||
- **Python 3** — the `agents-sync` hook is a Python script; `python3` must be on
|
||||
- **Python 3** — the two `agents-*` hooks are Python scripts; `python3` must be on
|
||||
`$PATH`. Nothing else here needs it.
|
||||
- **`tea`** (optional) — Gitea's own CLI, only for `/kettle:use`. `brew install
|
||||
tea`, or from [gitea.com/gitea/tea/releases](https://gitea.com/gitea/tea/releases).
|
||||
@@ -36,7 +36,8 @@ the tool.
|
||||
### Installing the binary
|
||||
|
||||
Building it needs **Go 1.26** — `cli/go.mod` says so because the Gitea SDK
|
||||
requires it. `vendor/` is committed, so the build itself needs no network.
|
||||
requires it. There is no `vendor/` directory: the first build downloads eight
|
||||
modules, verified against `go.sum`.
|
||||
|
||||
```bash
|
||||
# from a clone of this repository
|
||||
@@ -159,7 +160,7 @@ dependency come back as a question, not as a guess.
|
||||
agents/
|
||||
kettle-runner.md subagent (Haiku) that runs the commands
|
||||
hooks/
|
||||
hooks.json registers the PreToolUse hooks
|
||||
hooks.json registers the one hook
|
||||
agents-sync.sh keeps AGENTS.md real and CLAUDE.md a symlink to it
|
||||
skills/
|
||||
init/ /kettle:init — make a directory a project
|
||||
|
||||
@@ -158,4 +158,29 @@ kettle init --login noodles --repo claude-skills/marketplace # and point it at
|
||||
kettle init --at ~/code/x # initialize somewhere else
|
||||
kettle init --dry-run # say what it would do, touch nothing
|
||||
```
|
||||
|
||||
## `kettle version`
|
||||
|
||||
print the version this binary was built as
|
||||
|
||||
A binary that cannot say what it is, is a support problem: an operator with an
|
||||
old copy on PATH and a skill written against a newer one has no way to see the
|
||||
mismatch, and neither does anybody reading their transcript.
|
||||
|
||||
The version is stamped at link time. A build from source says "dev" and means
|
||||
it — that is not a placeholder to be edited, it is the answer for a binary that
|
||||
came out of somebody's working tree rather than off a tag.
|
||||
|
||||
The commit is reported when the build recorded one, which `go build` does from
|
||||
git and a build from an unpacked tarball cannot. A tree with uncommitted
|
||||
changes in it says so beside the commit.
|
||||
|
||||
| flag | default | what it does |
|
||||
| --- | --- | --- |
|
||||
| `--short` | `false` | print the version alone, with nothing around it |
|
||||
|
||||
```bash
|
||||
kettle version # the version, the toolchain and the commit
|
||||
kettle version --short # just the version, for a script
|
||||
```
|
||||
<!-- /kettle:gen -->
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# AGENTS.md — the tdl plugin
|
||||
|
||||
**Three Dots Labs Go conventions as an enforceable rule set.** One skill,
|
||||
`/tdl:audit`, which does two things with the same body of rules: audits a Go project
|
||||
against them, or scaffolds code that already follows them.
|
||||
|
||||
No binary, no hooks, no state, no network. Everything here is markdown an agent
|
||||
reads.
|
||||
|
||||
```
|
||||
.claude-plugin/plugin.json the manifest
|
||||
skills/audit/
|
||||
SKILL.md the procedure: parse args, load rules, audit or scaffold
|
||||
references/rules-*.md nine rule files, loaded in full before any operation
|
||||
templates/*.md nine scaffolds, one per generated artefact
|
||||
```
|
||||
|
||||
## The rules
|
||||
|
||||
Nine files, by subject: `architecture`, `domain`, `cqrs`, `repository`, `errors`,
|
||||
`ports`, `naming`, `codestyle`, `watermill`. They encode the conventions of the
|
||||
`wild-workouts-go-ddd-example` reference implementation and the four canonical Three
|
||||
Dots Labs articles — DDD Lite in Go, Introducing Clean Architecture, Basic CQRS in
|
||||
Go, Repository Pattern in Go.
|
||||
|
||||
**The skill loads all nine before it does anything**, in parallel, and the reason is
|
||||
that the rules cross-reference each other: a naming finding is only correct in the
|
||||
light of the layer the file sits in, and an audit that read one file would report
|
||||
violations of a rule it had not read the exception to.
|
||||
|
||||
Findings are reported **by severity**, and the severity is part of the rule, not a
|
||||
judgement made at report time. That is what makes the output usable: a run that
|
||||
flags everything equally is a run somebody stops reading.
|
||||
|
||||
## The templates
|
||||
|
||||
Nine, one per artefact the scaffolder can produce: `service`, `command`, `query`,
|
||||
`entity`, `repo`, `unified_server`, `watermill_router`, `event_handler`,
|
||||
`event_publisher`.
|
||||
|
||||
They exist so that "generate a handler" and "audit a handler" cannot disagree —
|
||||
**a template that would fail the audit is a bug in the template.** When a rule
|
||||
changes, the templates it touches change in the same edit; that is the plugin's only
|
||||
real invariant and nothing mechanical checks it.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/tdl:audit # audit the working directory
|
||||
/tdl:audit ./internal/orders # audit one path
|
||||
/tdl:audit scaffold command PlaceOrder # generate from a template
|
||||
/tdl:audit scaffold service Billing
|
||||
```
|
||||
|
||||
The scaffold types are listed in the skill's argument parsing, and an argument that
|
||||
matches no pattern gets usage help rather than a guess.
|
||||
|
||||
## Keeping this file true
|
||||
|
||||
- **Scope:** this plugin's layout, the two things the skill does, and the
|
||||
template ⇄ rule invariant. Files: `SKILL.md`, `references/`, `templates/`.
|
||||
- **Update it when** a rule file or a template is added or removed (both lists above
|
||||
name every one), a scaffold type is added, or the skill grows a third mode.
|
||||
- **Do not** copy rule content here. The references are the rules; this file says
|
||||
what they are for.
|
||||
Reference in New Issue
Block a user