Install
- Add the gago-plugins marketplace.
- Install the foghorn plugin.
- Install unmistakable and iceberg from it.
The checker also runs without Claude. It needs Node 22.18 or later.
/plugin marketplace add gagoar/gago-plugins
/plugin install foghorn@gago-plugins
/plugin install unmistakable@gago-plugins
/plugin install iceberg@gago-plugins
/foghorn:draft
Run /foghorn:draft <from>..<to>. Options: --voice <preset|path> and --out <file>.
- foghorn checks that
asd-ste100andiceberg:editare available. If one is missing, it stops and prints the install commands. - It collects. An agent that asks for read access only reads git history, the merged pull requests and the linked issues. It returns JSON.
- It shows you a table of the entries. You can correct a classification before it writes.
- It classifies each change as breaking, security, feature, fix or internal. It drops internal ones and tells you how many.
- It drafts each entry in the voice. Every entry that changed behavior gets a real before and after. foghorn reads the diff and the tests, or runs the code. It never invents an example.
- It assembles the notes. A breaking release gets a preface. Entries run in this order: breaking, security, feature, fix.
- It checks, polishes and checks again. The loop runs at most 3 rounds. Warnings do not block.
- It prints the notes. With
--outit writes the file you name.
Many people have more than one gh account. foghorn asks which account can read the repository. It runs each gh call through scripts/gh-read.ts, which uses the token of that account and allows only read-only forms. It never runs gh auth switch.
/foghorn:check
Run /foghorn:check RELEASE.md. It runs the checker and explains each finding in plain words. Hard failures come first. It changes nothing unless you ask.
A raw GitHub release body has no version or date in it. The checker warns about that. The warning is expected there.
/foghorn:voice
show prints the voice. pick writes a preset to ./foghorn.voice.json and asks before it overwrites a file. edit changes one field and checks the result. It refuses a field that is not in the schema.
The checks
Three checks are hard. They fail the run. A fourth, credit-unverified, is hard too and runs only with --handles.
breaking-no-preface: a release that says it is breaking has no preface line.behavior-no-example: an entry says "previously" or "now" and shows no old and new output.entry-no-link-or-credit: an entry has no issue, pull request or advisory link and no credit by handle.
Nine checks warn.
unlabeled-code-block: a code block has no label.marketing-word: a word from the banned list.entry-too-long: an entry is overmaxEntryChars, prose and code together.too-many-examples: more than 4 code blocks in one entry.wall-of-text: a paragraph over 900 characters.missing-version-or-date: the header lacks a version or a date.affects-line-missing: a risky entry has noAffects:line.padding: a filler opener.emoji: an emoji.
A hedge such as "in theory" is content. The checker never flags one.
Exit code 0 means no hard failure. Exit code 1 means a hard failure. Exit code 2 means a usage error. Use --json for machine output and --disable a,b to skip checks.
Config
The voice is a JSON file. Pick a preset with /foghorn:voice pick, or write foghorn.voice.json yourself. The schema is schema/foghorn-voice.schema.json. An example is foghorn.voice.example.json. The voice page lists every field.
node scripts/check-notes.ts --voice ./foghorn.voice.json RELEASE.md
Dependencies
The polish step uses two skills.
asd-ste100from the unmistakable plugin runs first, in STE-flavored mode.iceberg:editfrom the iceberg plugin runs second, with--no-em-dashand no other extended flag.
Why only that flag. --no-weakeners removes hedges, and a hedge in a release note is content. --strip-ai-commentary removes the first person. The foghorn voice uses it.
A polish pass must keep every link, credit, label and code block. foghorn restores any that it removes.
Read-only guarantees
- Read-only comes from the permission mode and the rules you run with, not from the agent file: Claude Code does not enforce a command pattern in an agent's
toolsline. The agent file removes the write tools, the Skill tool and the Agent tool. Keep the default permission mode. - The skills run only these shell commands:
git log,git show,git diff,git tag,git remote get-url, thegh-read.tswrapper (read-onlyghforms only; any otherghform prompts you) and the checker on a draft in its scratch directory. - foghorn writes a scratch file in a per-run directory under
~/.claude/plugins/data/, the file you name with--out, and./foghorn.voice.jsonwhen you pick a voice. It writes nothing else. - foghorn never changes your repository, your pull requests, your issues or your releases. It never publishes.
Privacy
foghorn has no server and sends no data of its own. The skills run on your machine through your Claude Code session. They read git history and, through the gh tool you have signed in to, pull requests and issues. That text goes to the model in your session, as any text in a session does.
FAQ
Does foghorn publish the release?
No. It prints a draft. You publish it.
Is foghorn better than generate-notes?
Not always. --generate-notes is instant and free. Use it when your pull request titles are good and your readers need a list. Use foghorn when readers need to know what changed for them.
Do I have to review the draft?
Yes. A model can misread a diff. The checker looks for an example. It cannot prove that the example is true.
What if foghorn cannot find a real example?
The entry has no example. foghorn never invents one.
Why does it drop some changes?
Internal changes do not matter to a user. That covers docs-site work, badges, artwork, CI gates and version bumps. A dependency bump that changes platform requirements stays in.
Can I use the checker without Claude?
Yes. Run node scripts/check-notes.ts --voice foghorn RELEASE.md.