homebrew-dev
Homebrew Packaging Skill
You are an expert at packaging software with Homebrew — both formulas (source builds and pre-built binaries) and casks (macOS apps, fonts, arbitrary files). You help users create, test, and distribute packages through personal taps on GitHub.
Deciding: Formula vs. Cask
| Scenario | Use |
|---|---|
| CLI tool with source code (C, Go, Rust, etc.) | Formula |
| Pre-built binary tarball for a CLI tool | Formula (no build step) |
macOS .app bundle |
Cask |
Font files (.ttf, .otf) |
Cask |
| PKG installer | Cask |
| Arbitrary file placement (e.g. completions, configs) | Cask with artifact |
| App with auto-updater | Cask with auto_updates true |
| Both a CLI and a GUI component | Both (linked from cask with binary) |
Creating a Formula
Scaffold
brew create https://example.com/mytool-1.0.tar.gz
This auto-populates url, sha256, version, and opens the editor.
Key fields
class Mytool < Formula
desc "One-line description (imperative, no trailing period, no 'A' prefix)"
homepage "https://example.com/mytool"
url "https://example.com/mytool-1.0.tar.gz"
sha256 "abc123..." # from: shasum -a 256 <file>
license "MIT"
version "1.0" # omit if parseable from url
depends_on "cmake" => :build # build-time only
depends_on "openssl@3" # runtime
depends_on "python@3.12" => :optional # optional runtime
depends_on :macos => :monterey # minimum macOS
def install
system "./configure", *std_configure_args
system "make", "install"
end
test do
system "#{bin}/mytool", "--version"
assert_match "1.0", shell_output("#{bin}/mytool --version")
end
end
Install directory variables
| Variable | Expands to |
|---|---|
bin |
$(brew --prefix)/bin |
lib |
$(brew --prefix)/lib |
include |
$(brew --prefix)/include |
share |
$(brew --prefix)/share |
etc |
$(brew --prefix)/etc |
var |
$(brew --prefix)/var |
libexec |
$(brew --prefix)/opt/<name>/libexec |
Build systems
# Autoconf/Make
system "./configure", *std_configure_args
system "make", "install"
# CMake
system "cmake", "-S", ".", "-B", "build", *std_cmake_args
system "cmake", "--build", "build"
system "cmake", "--install", "build"
# Cargo (Rust)
system "cargo", "install", *std_cargo_args
# Go
system "go", "build", *std_go_args, "-o", bin/"mytool", "./cmd/mytool"
# Pre-built binary (no build)
def install
bin.install "mytool"
man1.install "mytool.1"
end
Architecture conditionals
on_arm do
url "https://example.com/mytool-1.0-arm64.tar.gz"
sha256 "arm_sha..."
end
on_intel do
url "https://example.com/mytool-1.0-x86_64.tar.gz"
sha256 "intel_sha..."
end
Creating a Cask
Scaffold
brew create --cask https://example.com/MyApp-1.0.dmg
Artifact types and their uses
| Artifact | Use case | Installs to |
|---|---|---|
app "MyApp.app" |
macOS .app bundles |
/Applications |
font "MyFont.ttf" |
Font files | ~/Library/Fonts/ |
binary "mytool" |
Pre-built CLI tool from archive | $(brew --prefix)/bin/ |
artifact "myfile", target: "~/.config/myfile" |
Arbitrary file placement | Custom path |
pkg "MyApp.pkg" |
PKG installer | System (requires uninstall) |
suite "MyApp.app" |
Multi-app suites | /Applications |
Minimal cask (DMG with app)
cask "myapp" do
version "1.0"
sha256 "abc123..."
url "https://example.com/MyApp-#{version}.dmg"
name "MyApp"
desc "Does something useful"
homepage "https://example.com/myapp"
app "MyApp.app"
zap trash: [
"~/Library/Application Support/MyApp",
"~/Library/Preferences/com.example.myapp.plist",
]
end
Font cask
cask "font-my-font" do
version "2.0"
sha256 "abc123..."
url "https://example.com/MyFont-#{version}.zip"
name "My Font"
desc "A beautiful typeface"
homepage "https://example.com/myfont"
font "MyFont-Regular.ttf"
font "MyFont-Bold.ttf"
font "MyFont-Italic.ttf"
end
Arbitrary file placement
cask "mycli" do
version "1.0"
sha256 "abc123..."
url "https://example.com/mycli-#{version}.zip"
name "MyCLI"
desc "A command-line tool"
homepage "https://example.com/mycli"
binary "mycli"
artifact "mycli.zsh-completion", target: "#{HOMEBREW_PREFIX}/share/zsh/site-functions/_mycli"
artifact "mycli.fish", target: "#{HOMEBREW_PREFIX}/share/fish/vendor_completions.d/mycli.fish"
end
PKG installer
cask "myapp" do
version "1.0"
sha256 "abc123..."
url "https://example.com/MyApp-#{version}.pkg"
name "MyApp"
desc "Does something useful"
homepage "https://example.com/myapp"
pkg "MyApp.pkg"
uninstall pkgutil: "com.example.myapp"
zap trash: [
"~/Library/Application Support/MyApp",
"~/Library/Preferences/com.example.myapp.plist",
]
end
Architecture-conditional cask
cask "myapp" do
version "1.0"
on_arm do
url "https://example.com/MyApp-#{version}-arm64.dmg"
sha256 "arm_sha..."
end
on_intel do
url "https://example.com/MyApp-#{version}-x86_64.dmg"
sha256 "intel_sha..."
end
name "MyApp"
desc "Does something useful"
homepage "https://example.com/myapp"
app "MyApp.app"
end
Rolling release (no stable version)
cask "myapp" do
version :latest
sha256 :no_check
url "https://example.com/MyApp-latest.dmg"
name "MyApp"
desc "Does something useful"
homepage "https://example.com/myapp"
auto_updates true # app has built-in updater
app "MyApp.app"
end
Bumping a Version
When a new release is out, update three things:
Formula:
url "https://example.com/mytool-1.1.tar.gz" # new URL
sha256 "newsha256..." # recompute (see below)
version "1.1" # if not parseable from url
Cask: same fields, but also verify #{version} interpolation in url still produces the correct download URL with the new version string.
Get the new checksum:
# Option A: download and compute locally
shasum -a 256 mytool-1.1.tar.gz
# Option B: let brew fetch it
brew fetch --force --build-from-source ./Formula/mytool.rb
Then verify:
brew audit mytool
brew test mytool
Faster alternative — let brew do it all:
brew bump-formula-pr mytool --version 1.1 --url https://example.com/mytool-1.1.tar.gz
brew bump-cask-pr myapp --version 1.1
These commands fetch the new tarball, compute the sha256, update the formula/cask, and open a pull request — all in one step. Recommended when submitting bumps to homebrew-core or homebrew-cask.
Submitting to Homebrew Core / Cask
To get your package into the official homebrew/core or homebrew/cask taps (so users can brew install without adding your tap), you need to open a PR against the upstream repo.
Eligibility requirements, the fork/PR workflow, audit flags, commit conventions, and common rejection reasons are covered in full at:
references/homebrew-core-submission.md
Setting up a Personal Tap
1. Create the GitHub repo
Name it homebrew-<tapname> (e.g. homebrew-tools).
2. Directory structure
homebrew-tools/
├── Formula/
│ └── mytool.rb
└── Casks/
└── myapp.rb
3. Tap and install
brew tap username/tools # taps github.com/username/homebrew-tools
brew install username/tools/mytool
brew install --cask username/tools/myapp
4. Test before publishing
brew install --build-from-source ./Formula/mytool.rb
brew install --cask ./Casks/myapp.rb
Development Commands
# Create scaffolds
brew create https://example.com/tool-1.0.tar.gz
brew create --cask https://example.com/App-1.0.dmg
# Edit formula/cask
brew edit mytool
brew edit --cask myapp
# Install for testing
brew install --build-from-source ./Formula/mytool.rb
brew install --cask ./Casks/myapp.rb
# Run tests
brew test mytool
brew test --cask myapp
# Audit (validate before publishing)
brew audit --new mytool
brew audit --new --cask myapp
# Uninstall / reinstall cycle
brew uninstall mytool && brew install --build-from-source ./Formula/mytool.rb
# Style check
brew style ./Formula/mytool.rb
brew style --cask ./Casks/myapp.rb
# Fetch without installing (verify download)
brew fetch mytool
brew fetch --cask myapp
Checksum Workflow
# Compute SHA-256 for a local file
shasum -a 256 myapp-1.0.dmg
# Or let brew fetch and show it
brew fetch --force --build-from-source ./Formula/mytool.rb
brew create auto-populates sha256 if it can download the file during scaffold creation.
Validation
Run before publishing to a tap:
brew audit --new mytool # stricter checks for new packages
brew audit --new --cask myapp
brew style ./Formula/mytool.rb # RuboCop style check
Common audit issues:
descmust not start with "A" or "The", must not end with a periodhomepagemust use HTTPSlicenserequired for formulas (use SPDX identifiers:"MIT","Apache-2.0", etc.)urlmust be stable, versioned, and use HTTPSsha256must match the download exactly- Test block required for formulas
References
- Formula patterns & annotated examples:
references/formula-cookbook.md - Cask patterns & annotated examples:
references/cask-cookbook.md - Submitting to homebrew-core / homebrew-cask:
references/homebrew-core-submission.md - Official docs: https://docs.brew.sh/Formula-Cookbook
- Cask docs: https://docs.brew.sh/Cask-Cookbook
More from trtmn/agent-skills
self-improvement
Run the self-improvement agent to review this session and the ~/.learnings/ log files. Use this skill whenever the user explicitly asks to review learnings, promote entries to CLAUDE.md, do an end-of-session review, or analyze GitHub PRs/issues for recurring patterns. Also use when the user says "promote", "review learnings", "what have we learned", or "self-improvement". Do NOT use this skill just for logging — logging happens automatically without the skill (see Passive Logging below). This skill is specifically for the *review and promotion* workflow.
20cowsay
Generates an ASCII cow saying custom text. Use when the user wants "cowsay", "cow say", or a cow to say something.
12unifi-api
Query and control a UniFi network using the `unifi` CLI (a restish wrapper with 1Password auth) or the REST API as fallback. Use this skill whenever the user wants to manage their UniFi network — listing connected clients, blocking/unblocking devices, managing firewall policies, checking WAN health and speed test results, rebooting devices, managing VLANs or SSIDs, reading traffic stats, port forwarding, or any other UniFi network management task. Prefer the `unifi` CLI for Integration API endpoints; fall back to raw curl/python for legacy API endpoints. Trigger even if the user doesn't say "API" or "UniFi" — phrases like "check my network", "block that device", "show me who's connected", "add a firewall rule", "what's my WAN IP", "how's my internet speed", or "what's on the guest network" are all good triggers.
5tailscale-policy-manager
>
5obsidian-cli
>
4skills-manager
Install, remove, list, find, and update Claude Code skills using the `npx skills` CLI. Use this skill whenever the user wants to manage their agent skills — install a new skill, search the skills registry, remove a skill, check for updates, or update all skills. Also trigger for requests like "find me a skill for X", "install the Y skill", "what skills do I have installed", "remove the Z skill", or "are my skills up to date.
4