| name | homebrew-formula-expert |
| description | This skill should be used when the user asks to "create a Homebrew formula", "fix brew audit errors", "write a formula", "Homebrew best practices", or needs help with Homebrew formula syntax, dependencies, and troubleshooting. Expert knowledge of Homebrew formula creation and maintenance. |
| category | distribution |
Homebrew Formula Expert
Deep expertise in Homebrew formula creation, maintenance, and best practices.
Surface scope: Homebrew installs plugins to the Claude Code CLI surface
(~/.claude/). Claude Desktop (DXT/MCPB extensions — a different, MCP-server
format) and Cowork are separate surfaces; see dist-extras and
commands/dist/surfaces.md for the full model.
Formula Anatomy
class MyApp < Formula
desc "Short description (< 80 chars, no 'A' or 'An' prefix)"
homepage "https://github.com/user/repo"
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
sha256 "64-char-hex-string"
license "MIT"
head "https://github.com/user/repo.git", branch: "main"
depends_on "python@3.12"
depends_on "cmake" => :build
def install
end
test do
end
end
Language-Specific Patterns
Python (virtualenv)
class MyPythonApp < Formula
include Language::Python::Virtualenv
desc "My Python application"
homepage "https://github.com/user/repo"
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
sha256 "..."
license "MIT"
depends_on "python@3.12"
resource "requests" do
url "https://files.pythonhosted.org/..."
sha256 "..."
end
def install
virtualenv_install_with_resources
end
test do
assert_match version.to_s, shell_output("#{bin}/myapp --version")
end
end
Node.js
class MyNodeApp < Formula
desc "My Node.js application"
homepage "https://github.com/user/repo"
url "https://registry.npmjs.org/myapp/-/myapp-1.0.0.tgz"
sha256 "..."
license "MIT"
depends_on "node"
def install
system "npm", "install", *std_npm_args
bin.install_symlink Dir["#{libexec}/bin/*"]
end
test do
assert_match version.to_s, shell_output("#{bin}/myapp --version")
end
end
Go
class MyGoApp < Formula
desc "My Go application"
homepage "https://github.com/user/repo"
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
sha256 "..."
license "MIT"
depends_on "go" => :build
def install
system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
end
test do
assert_match version.to_s, shell_output("#{bin}/myapp --version")
end
end
Rust
class MyRustApp < Formula
desc "My Rust application"
homepage "https://github.com/user/repo"
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
sha256 "..."
license "MIT"
depends_on "rust" => :build
def install
system "cargo", "install", *std_cargo_args
end
test do
assert_match version.to_s, shell_output("#{bin}/myapp --version")
end
end
Best Practices
Description (desc)
desc "Terminal optimizer for AI-assisted development"
desc "A terminal optimizer for AI-assisted development"
desc "aiterm is a terminal optimizer..."
desc "..."
URL Patterns
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
url "https://github.com/user/repo/archive/refs/tags/v1.0.0.tar.gz"
url "https://files.pythonhosted.org/packages/.../myapp-1.0.0.tar.gz"
url "https://registry.npmjs.org/myapp/-/myapp-1.0.0.tgz"
SHA256 Calculation
curl -sL https://github.com/user/repo/archive/v1.0.0.tar.gz | shasum -a 256
shasum -a 256 myapp-1.0.0.tar.gz
License (SPDX identifiers)
license "MIT"
license "Apache-2.0"
license "GPL-3.0-or-later"
license any_of: ["MIT", "Apache-2.0"]
Dependencies
depends_on "python@3.12"
depends_on "cmake" => :build
depends_on "imagemagick" => :optional
depends_on "jq" => :recommended
depends_on "linux-headers" => :build if OS.linux?
Common Issues & Fixes
Issue: Python dependencies missing
resource "click" do
url "https://files.pythonhosted.org/packages/.../click-8.0.0.tar.gz"
sha256 "..."
end
def install
venv = virtualenv_create(libexec, "python3.12")
venv.pip_install "click>=8.0"
venv.pip_install "rich>=13.0"
venv.pip_install buildpath
end
Issue: Binary not in PATH
def install
bin.install_symlink libexec/"bin/myapp"
end
Issue: Test fails
test do
(testpath/".config/myapp").mkpath
(testpath/".config/myapp/config.json").write("{}")
assert_match version.to_s, shell_output("#{bin}/myapp --version")
end
Issue: brew audit failures
brew audit --strict --online Formula/myapp.rb
Tap Management
Create Tap Repository
homebrew-tap/
├── Formula/
│ ├── myapp.rb
│ └── othertool.rb
└── README.md
Update Formula
url "https://github.com/user/repo/archive/v1.0.0.tar.gz"
sha256 "old-sha256"
url "https://github.com/user/repo/archive/v1.0.1.tar.gz"
sha256 "new-sha256"
Testing Locally
brew install --build-from-source ./Formula/myapp.rb
brew reinstall --build-from-source ./Formula/myapp.rb
brew uninstall myapp
Formula Template
class {{ClassName}} < Formula
{{
include Language::Python::Virtualenv
{{/if}}
desc "{{description}}"
homepage "{{homepage}}"
url "{{url}}"
sha256 "{{sha256}}"
license "{{license}}"
{{#each dependencies}}
depends_on "{{this}}"
{{/each}}
def install
{{install_commands}}
end
test do
assert_match version.to_s, shell_output("#{bin}/{{binary}} --version")
end
end
Claude Code Plugin Formula
For Claude Code plugins, use this special pattern with Claude detection:
class MyPlugin < Formula
desc "My plugin - Claude Code plugin"
homepage "https://github.com/user/my-plugin"
url "https://github.com/user/my-plugin/archive/refs/tags/v1.0.0.tar.gz"
sha256 "..."
license "MIT"
depends_on "jq" => :optional
def install
libexec.install Dir["*", ".*"].reject { |f| %w[. .. .git].include?(f) }
(bin/"my-plugin-install").write <<~EOS
#!/bin/bash
PLUGIN_NAME="my-plugin"
TARGET_DIR="$HOME/.claude/plugins/$PLUGIN_NAME"
SOURCE_DIR="$(brew --prefix)/opt/my-plugin/libexec"
echo "Installing plugin to Claude Code..."
mkdir -p "$HOME/.claude/plugins" 2>/dev/null || true
if [ -L "$TARGET_DIR" ] || [ -d "$TARGET_DIR" ]; then
rm -rf "$TARGET_DIR" 2>/dev/null || true
fi
ln -sf "$SOURCE_DIR" "$TARGET_DIR"
# Claude detection - skip auto-enable if Claude is running
SETTINGS_FILE="$HOME/.claude/settings.json"
AUTO_ENABLED=false
CLAUDE_RUNNING=false
if command -v lsof &>/dev/null; then
if lsof "$SETTINGS_FILE" 2>/dev/null | grep -q "claude"; then
CLAUDE_RUNNING=true
fi
elif pgrep -x "claude" >/dev/null 2>&1; then
CLAUDE_RUNNING=true
fi
if [ "$CLAUDE_RUNNING" = false ] && command -v jq &>/dev/null && [ -f "$SETTINGS_FILE" ]; then
TEMP_FILE=$(mktemp)
if jq --arg plugin "${PLUGIN_NAME}@local-plugins" '.enabledPlugins[$plugin] = true' "$SETTINGS_FILE" > "$TEMP_FILE" 2>/dev/null; then
mv "$TEMP_FILE" "$SETTINGS_FILE" 2>/dev/null && AUTO_ENABLED=true
fi
[ -f "$TEMP_FILE" ] && rm -f "$TEMP_FILE" 2>/dev/null
fi
echo "✅ Plugin installed!"
if [ "$AUTO_ENABLED" = true ]; then
echo "Plugin auto-enabled."
elif [ "$CLAUDE_RUNNING" = true ]; then
echo "Claude running - skipped auto-enable."
echo "Run: claude plugin install ${PLUGIN_NAME}@local-plugins"
else
echo "Run: claude plugin install ${PLUGIN_NAME}@local-plugins"
fi
EOS
chmod "+x", bin/"my-plugin-install"
end
def post_install
system bin/"my-plugin-install"
system "claude", "plugin", "update", "my-plugin@local-plugins" if which("claude")
rescue
nil
end
test do
assert_predicate libexec/".claude-plugin/plugin.json", :exist?
end
end
Why Claude Detection?
| Issue | Without Detection | With Detection |
|---|
| Claude running | Hangs indefinitely | Skips instantly |
| Install time | 2+ minutes | 2-3 seconds |
| User experience | Confusing hang | Clear message |
Key Components
lsof check - Detects if Claude has settings.json open
pgrep fallback - Checks if claude process is running
- Skip logic - Bypasses settings.json modification if Claude running
- Clear messaging - Tells user exactly what to do
Integration
Use with:
/craft:dist:homebrew - Generate formula automatically
/craft:check --for release - Validate before formula update
/craft:git:tag - Create version for formula