--- name: kettle-project description: Generated flag reference for the project-level `kettle` commands — `kettle init`, `kettle auth`, `kettle config`, `kettle gen`, `kettle mirror`. Load it to look up the exact flags and defaults of one of those, or when a command answers "no project" / "no login" and you need `kettle config` to say what this directory resolved to. The rules around initializing are /kettle:init and the credential workflow is /kettle:auth; this file is the flag table both of them point at. --- # kettle project — the project itself `kettle` resolves everything from one marker. `/.kettle/` is created by `kettle init` and never inferred: `.git` is in every clone, so a tool that guessed a root from one would write issues into whatever tree it happened to be standing in. With no marker anywhere the command stops and names the directories it searched — that is an answer, not a fallback. Two configuration files, and the split is the point. `/.kettle/config.yaml` holds the tracker repository and the **name** of a login; the name is worth nothing on its own, which is what makes it safe inside a working tree. `~/.config/kettle/logins.yaml` (0600, one per machine, `$KETTLE_CONFIG_HOME` or `$XDG_CONFIG_HOME` move it) holds the tokens. `KETTLE_LOGIN`, `KETTLE_REPO`, `KETTLE_URL` and `KETTLE_TOKEN` each override the file they shadow. **No `kettle` on PATH?** `command not found: kettle` is the whole story — no script and no other CLI substitutes for it, and it means the binary that wrote this file has since moved or been removed, because nothing else could have put it here. Stop and tell the operator to reinstall it: `go install git.noodles.cam/claude-skills/marketplace/cli/cmd/kettle@latest`, or `cd cli && make install` in a checkout of that repository (go.mod requires **go 1.26**). **This file is written by the binary and is replaced whole on the next `kettle gen scaffold`.** Every word of it — the prose as much as the flag table below — is embedded in the `kettle` that wrote it, which is why the two cannot disagree about a flag. Editing it here changes nothing durable; the fix for a wrong sentence is a newer `kettle`, and the fix for a stale one is `kettle gen scaffold`. `kettle config` says which version wrote this tree. **Generated from the kettle command registry by `kettle gen scaffold`.** Everything between the two markers is replaced on the next run — the prose around it is embedded in the binary and replaced with it. ## `kettle auth list | add | remove ` manage the tokens this machine holds Credentials live in one file per machine, outside every working tree, mode 0600. A project pins a login by NAME; the name is worth nothing on its own, which is what makes it safe to keep in a file inside the repository. The token is read from standard input unless --token is given, because an argument is in the shell history the moment it is typed: kettle auth add --name noodles --url https://git.example.com < token.txt pass show gitea/token | kettle auth add --name noodles --url https://git.example.com `list` never prints a token. There is no flag to make it. --scopes RECORDS WHAT THE TOKEN WAS MINTED WITH, and records is all it does: nothing is checked against it and nothing is refused because of it. It is worth writing down because the instance will not answer the question — Gitea's own token listing needs a password, not a token, so a token cannot be asked what it may do. Gitea spells them :; issues need `write:issue`, and everything `kettle api` reaches outside issues — releases, pull requests, branches, tags, actions — is `repository`. A token minted for issues alone answers 403 there, and the 403 names no scope. | flag | default | what it does | | --- | --- | --- | | `--name` | — | login name (add) | | `--scopes` | — | what the token was minted with, comma separated, e.g. write:issue,write:repository; documentation only (add) | | `--token` | — | token, if you would rather not use stdin (add) | | `--url` | — | instance URL, e.g. https://git.example.com (add) | | `--user` | — | account this token belongs to; documentation only (add) | ```bash kettle auth list # what this machine holds pass show gitea | kettle auth add --name noodles --url https://git.example.com # add one, token on stdin kettle auth add --name noodles --url https://git.example.com --scopes write:issue,write:repository < t.txt # and write down what it can do kettle auth remove noodles # forget it ``` ## `kettle config` show what this project resolved to Every path and every setting, with the overrides already applied, so a run that went somewhere unexpected can be explained without guessing. The token is never printed — only whether one was found. This is the command to reach for when the store looks empty, when a push says 401, or when two directories disagree about which project they are in. ```bash kettle config # resolved paths and settings ``` ## `kettle gen scaffold` write this project's .claude/ commands, skills and subagent A skill tells an agent how to invoke this binary, and a command is how an operator invokes one by hand. Both are written from here, whole, because both travel INSIDE the binary: the prose is embedded next to the code it describes and the flag tables are rendered from the command registry the binary is built from, so neither can be a version behind the other. That is the whole reason these documents are not a plugin any more. A plugin ships on its own cadence, and nothing on an operator's machine ever checked that the one they installed described the binary they installed — so a renamed flag could still arrive with documentation recommending the old one, which is exactly the failure the generated block was invented to prevent, one hop further downstream. EVERY FILE IS WRITTEN WHOLE, and that is a deliberate reversal. The old generator owned a region and left every byte outside it alone, because the prose around the block was somebody's hand-written file. It is not any more: it is embedded, so there is no hand-written half left to protect, and preserving local edits would mean freezing a project's documentation at whatever version first initialized it. The markers stay in the output so a reader can see which half came from the registry. WHAT THIS MEANS FOR A LOCAL EDIT: it does not survive. Run --check before an upgrade if you have made one; the fix for a sentence that is wrong is a newer kettle, not a patch that the next run silently discards. The output is deterministic to the byte — no timestamps, no map iteration — so regenerating something that has not changed produces no diff. --check is that property made useful: it writes nothing and exits 1 when any file on disk differs from what would be written, which is what a pre-commit hook or a CI step calls. It wins over --dry-run when both are given. | flag | default | what it does | | --- | --- | --- | | `--check` | `false` | write nothing, exit 1 if anything is out of date | | `--dry-run` | `false` | print what would change; write nothing | | `--out` | — | directory to write into (default: /.claude) | ```bash kettle gen scaffold # write .claude/ under this project kettle gen scaffold --out ~/code/x/.claude # write it somewhere else kettle gen scaffold --dry-run # print what would change; write nothing kettle gen scaffold --check # exit 1 if the documents are out of date ``` ## `kettle init` make this directory a project that tracks issues Creates `.kettle/` — the marker every other command resolves the store from, and `.kettle/config.yaml`, which says which tracker repository these issues belong to and which login to reach it under — and writes `.claude/`: the slash commands an operator invokes, the skills a model loads, and the runner subagent. The marker is deliberately something an operator makes, not something inferred from the tree: `.git` is in every clone, so anything that inferred a root from one would write issues into whatever it happened to be installed in. --login pins a name, never a credential. The tokens live in one file per machine, outside every working tree, managed with `kettle auth`. All of it is idempotent: it creates .kettle/issues and .kettle/payload, migrates an older store in if it finds one (either layout the tea plugin used, oldest first), writes the config without disturbing settings it was not given, writes the .claude/ tree, and adds .kettle/ to .gitignore. Each migration is a move, not a copy — two stores is the state the marker exists to prevent — and it refuses to pick a winner when both sides hold a file of the same name. IT REFUSES TO RUN IN A LINKED WORKTREE, and names the main checkout instead. A worktree is the same project on another branch and reaches the store by a hop out to the main checkout; a marker here would give one project two stores, and the directory holding the second one disappears with the branch. --interactive walks a person through the whole thing — the login, the token with the echo turned off, the repository, the .claude/ tree and the AGENTS.md mirror hook. IT REQUIRES A TERMINAL and refuses a standard input that is not one, which is deliberate: every question it asks has a flag beside it, so nothing that is not a person ever needs to answer a prompt. | flag | default | what it does | | --- | --- | --- | | `--at` | — | directory to initialize (default: the working directory) | | `--dry-run` | `false` | report what would happen; change nothing | | `--force-settings` | `false` | let the hook be merged into an existing settings.json, reformatting it | | `--interactive` | `false` | ask, one question at a time; requires a terminal | | `--login` | — | name of a login in the machine-wide file (see `kettle auth`) | | `--mirror-hook` | `false` | register `kettle mirror --hook` on PreToolUse(Bash) | | `--no-scaffold` | `false` | do not write the .claude/ commands, skills and subagent | | `--repo` | — | tracker repository, as owner/name | | `--scaffold-out` | — | where the .claude/ tree goes (default: /.claude) | ```bash kettle init # initialize the current directory kettle init --interactive # be walked through it, at a terminal kettle init --login noodles --repo claude-skills/marketplace # and point it at a tracker kettle init --mirror-hook # register the AGENTS.md mirror on PreToolUse(Bash) kettle init --at ~/code/x # initialize somewhere else kettle init --dry-run # say what it would do, touch nothing ``` ## `kettle mirror []` keep CLAUDE.md a symlink to AGENTS.md in every directory below here Two agent harnesses read two different filenames for the same document. A repository that keeps both as real files keeps TWO DOCUMENTS, and they drift — silently, until somebody reads the stale one and believes it. This walks a tree and leaves one arrangement behind everywhere: AGENTS.md is the real file; CLAUDE.md is a symlink pointing at it. The link is relative, so a tree that is moved, copied or cloned keeps working. AGENTS.md is the real one because the convention is not one vendor's: a repository that names its documents after a single tool has picked a side it did not need to pick. NOTHING HERE DELETES CONTENT. Six of the seven states it can find are repaired losslessly — a missing link is created, a reversed layout is swapped round, a duplicate whose bytes match its original is replaced by the link. The seventh, two real files whose contents DIFFER, is reported and left exactly as it was: one of them is somebody's writing and no rule here knows which. It walks the directory given, or the working directory. node_modules, vendor, venv, __pycache__ and every dot-directory are skipped, because somebody else's tree is somebody else's business. --hook is the PreToolUse form: it reads the hook payload on standard input, writes any report back as additionalContext, and ALWAYS EXITS 0 — including when it fails. A tool that broke somebody's Bash call because its documentation helper crashed would be worse than no tool. --check is the opposite end: it writes nothing and exits 1 when the tree is not canonical, which is what a pre-commit hook or a make target calls. `kettle init --interactive` offers to register the --hook form in .claude/settings.json. It is offered rather than assumed: this is one repository's documentation convention, and a project that does not keep AGENTS.md files wants nothing to do with it. | flag | default | what it does | | --- | --- | --- | | `--check` | `false` | write nothing, exit 1 if the tree is not canonical | | `--hook` | `false` | PreToolUse form: payload on stdin, report as additionalContext, always exit 0 | | `--quiet` | `false` | repair without printing what was repaired | ```bash kettle mirror # repair the working directory and everything below it kettle mirror ~/code/x # repair somewhere else kettle mirror --check # exit 1 if anything is out of place; write nothing kettle mirror --hook # the PreToolUse form; reads a payload, always exits 0 ``` ## `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 ```