Most importantly, run ./bin/brew lgtm (style checks, type checks and tests) to verify any file edits before prompting for input.
Inline any new or existing methods or variables used only once unless they are needed for unit tests. Keep the diff as small, DRY and YAGNI as possible. Re-read relevant files after each prompt and preserve user edits and comments. Before finishing, check your work and point out anything the user may not have considered.
This is a Ruby repository with Bash scripts for faster execution that provides the brew command for the Homebrew package manager.
- Use
./bin/brew, never a systembrewonPATH. - Never use the system Ruby; it is an incompatible older version. Run Ruby with
./bin/brew ruby -- <args>to load Homebrew's vendored Ruby and libraries.
./bin/brew typecheck: Sorbet type checking. It only runs globally but is fast../bin/brew style --fix --changed: RuboCop linting. Pass files to check them individually, e.g../bin/brew style --fix Library/Homebrew/cmd/reinstall.rb../bin/brew tests --online --changed: RSpec tests. Online tests can be flaky, so ignore failures that pass on a rerun. Pass--onlyfor individual files, e.g../bin/brew tests --only=cmd/reinstallrunsLibrary/Homebrew/test/cmd/reinstall_spec.rb../bin/brew lgtm --onlineruns all of the above in one command.- The Homebrew MCP Server (
./bin/brew mcp-server) can also run all of the above.
- Inspect
git diffand keep it focused. - Never commit to
main. Branch fromorigin/HEADwith a relevant name without category prefixes such asfix/orchore/, replacing any autogenerated branch name before its first commit. - Amend the existing commit for related fixes and update its message rather than adding a follow-up commit.
- Use a commit subject under 51 characters without Conventional Commit prefixes such as
feat:,fix:orchore:; theCommit StyleGitHub Actions workflow rejects them. When a body is useful, use a dash list with lines under 73 characters focused on why and wrap filenames, code and identifiers in backticks. Pass multiline messages throughgit commit -Frather than literal\n. - Never add
Co-Authored-Byor other AI attribution trailers to commits or pull requests and never GPG-sign agent commits. - Fill in the pull request template from
.github/PULL_REQUEST_TEMPLATE.md; never bypass it with e.g.gh pr create --fill.
- Maintain existing code structure and organisation and document public APIs.
- Use Sorbet
sigtype signatures andtyped: strictfor new files. - Never use
T.must(enforced bySorbet/ForbidTMust): bind the value to a local and branch onnil, usefetchorfirst(n)on collections, or raise an exception with a helpful message wherenilis impossible. - Never use
T.unsafe(enforced bySorbet/ForbidTUnsafe): give DSL parameters= nildefaults with nilable types, narrow withis_a?orcase, userequires_ancestorfor typed modules andpublic_sendbehind arespond_to?guard for duck typing. - Avoid
T.cast,T.let,T.untypedandT.anything: use precise types and APIs that return non-nil values. If a generic top type is unavoidable, useT.anythingrather thanT.untyped. - Never use
.send: call methods directly, use.public_sendonly for dynamically-named public methods and make private methods public in tests (enforced byHomebrew/NoSendInTests). In tests, read and write state through publicattr_*accessors rather thaninstance_variable_get/instance_variable_set(enforced byHomebrew/NoInstanceVariableAccessInTests). - Shell out via
HOMEBREW_BREW_FILEinstead of requiringcmd/ordev-cmdwhen composing brew commands. - Keep
extend/os/*prepends thin: put theprependin the OS-specificlinuxormacosfile rather than the shared loader with an inlineif, and put substantive logic in shared code outsideextend/so it is tested on all platforms rather than in:needs_linuxor:needs_macosspecs. - When Bash logic mirrors Ruby logic, keep both in sync with two-way comments naming the matching Ruby and Bash locations and keep matching helper filenames aligned.
- Use
&>/dev/nullinstead of>/dev/null 2>&1. - Put a comment immediately above each
shellcheck disableexplaining why it is needed. - Prefer self-documenting code through strings and variable names to new explanatory comments.
- Use UK spelling and punctuation; avoid em dashes and Oxford commas.
- Wrap human-written user-facing terminal output at around 80 characters; generated output and code are exempt.
- Update
docs/when the behaviour it describes changes.
- Write unit tests for new functionality. Try
typed: strictfirst and revert totyped: trueif the errors are not easily fixable; never usetyped: false. - For bug fixes and regressions, use red-green TDD and change the fewest lines needed.
- When adding or tightening tests, verify them with a red/green cycle using the exact
--only=file:linetarget for the example you changed. - Use at most one
:integration_testper command: a fast happy-path test. Add another only for essential core functionality in essential non-developer commands. - Use a single
expectper unit test; combine expectations only in integration tests or when thebeforesetup is non-trivial. - Formula classes created in specs may be frozen, so stub instances rather than class methods.
Library/Homebrew/test/spec_helper.rbsnapshots and restoresENVaround every example, so set environment variables directly withoutwith_envor manual cleanup.
bin/brew: Homebrew'sbrewcommand main Bash entry point script.completions/andmanpages/: generated shell completions andmanpages. Don't edit directly; regenerate with./bin/brew generate-man-completions.Library/Homebrew/: Homebrew's core Ruby (with a little Bash) logic.Library/Homebrew/bundle/: Homebrew'sbrew bundlecommand.Library/Homebrew/cask/: Homebrew's Cask classes and DSL.Library/Homebrew/extend/os/: Homebrew's OS-specific (i.e. macOS or Linux) class extension logic.Library/Homebrew/formula.rb: Homebrew's Formula class and DSL.docs/: Documentation for Homebrew users, contributors and maintainers. Consult these for best practices and help.package/: Files to generate the macOS.pkgfile.