# New Cask

> This skill should be used when the user asks to "add a cask to homebrew", "create a homebrew cask", "add an app to homebrew", "new cask for", "homebrew cask for", "package an app for homebrew", "submit to homebrew-cask", "make a cask", "contribute a cask", or wants to package a macOS application for Homebrew.

- Skill: `majiayu000/new-cask` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/new-cask`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/new-cask/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/new-cask

---


# Creating a New Homebrew Cask

## Reference Documentation

Official docs (read as needed):

- `$(brew --repository)/docs/Adding-Software-to-Homebrew.md` - Contribution overview
- `$(brew --repository)/docs/Cask-Cookbook.md` - Stanza reference
- `$(brew --repository)/docs/Brew-Livecheck.md` - Livecheck strategies
- `$(brew --repository)/docs/Acceptable-Casks.md` - Acceptance criteria

## Workflow Overview

1. **Pre-flight checks** - Duplicate detection, rejection history, acceptability
2. **Information gathering** - URLs, versions, checksums, auto-updates, zap paths
3. **Cask creation** - Write the .rb file with proper stanzas
4. **Testing** - Audit, style, livecheck, install/uninstall
5. **PR submission** - Branch, commit, push, create PR with AI disclosure

## Pre-flight Checks

Before investing time:

1. **Check for duplicates**: Search existing casks and open PRs

   ```bash
   brew search <name>
   gh pr list -R Homebrew/homebrew-cask --search "<name>"
   ```

2. **Check rejection history**: Some apps are explicitly rejected

   ```bash
   gh issue list -R Homebrew/homebrew-cask --search "<name> is:closed"
   ```

3. **Verify acceptability**: Review `$(brew --repository)/docs/Acceptable-Casks.md`
   - No trial-only software
   - No malware or potentially unwanted programs
   - Must have stable releases

### Token Collisions

When an app name conflicts with an existing cask:

1. Check what the existing cask is: `brew info --cask <name>`
2. If different products (e.g., CLI tool vs GUI app from same vendor), suffix appropriately:
   - GUI app when CLI exists: `<name>-app`
   - Different vendor's product: `<vendor>-<name>`
3. Example: `codex` (CLI) vs `codex-app` (GUI desktop app)

### URL Types

The skill accepts various URL types:

- **Direct download URL** (preferred): `https://example.com/App.dmg`
- **Product/download page**: Will scrape for download links
- **GitHub repo**: Will check releases for download assets
- **No URL**: Will ask for app name and search for download source

If the URL isn't a direct download, first identify the actual installer URL before proceeding with checksum calculation.

## Information Gathering

### 1. Find Download URLs

Look for direct download links on the vendor's website. Check for architecture-specific URLs:

- Apple Silicon (arm64): URLs often contain `arm64`, `aarch64`, `apple`, or `universal`
- Intel (x64/x86_64): URLs often contain `x64`, `x86_64`, `intel`

**Common URL patterns:**

- `/latest` redirects (use `strategy :header_match` for livecheck)
- RELEASES.json feeds (use `strategy :json` for livecheck)
- GitHub releases (use `strategy :github_latest` for livecheck)

### 2. Discover Version Numbers

Try these approaches:

1. Check URL structure - version often in filename
2. Follow `/latest` URLs and inspect redirect:
   ```bash
   curl -sIL "<url>/latest" | grep -i location
   ```
3. Look for RELEASES.json, releases.json, or similar feeds
4. Check app's "About" or website changelog

**Important**: When versions differ by architecture, use `on_arm`/`on_intel` blocks.

### 3. Calculate Checksums

Download and checksum each architecture:

```bash
# ARM64
curl -Lo /tmp/app-arm.dmg "<arm64-url>"
shasum -a 256 /tmp/app-arm.dmg

# Intel
curl -Lo /tmp/app-intel.dmg "<intel-url>"
shasum -a 256 /tmp/app-intel.dmg
```

**Unversioned URLs**: When the download URL doesn't contain version info (always points to latest), use `sha256 :no_check` instead of a checksum.

### 4. Detect Auto-updates

Check if the app self-updates (affects `auto_updates` stanza):

- Look for "Check for Updates" in app menus
- Check for Sparkle framework: `ls "/Applications/<App>.app/Contents/Frameworks" | grep -i sparkle`
- Look for ShipIt (Electron apps): bundle ID contains `.ShipIt`
- Check Info.plist for `SUFeedURL` (Sparkle appcast)

If auto-updates exist, add `auto_updates true` to the cask.

### 5. Discover Zap Paths

Use the `brew createzap` helper (requires tapping first):

```bash
brew tap nrlquaker/createzap
brew createzap "<app-name>"
brew createzap "com.example.bundle-id"  # Try bundle ID if app name fails
```

**Important**: Ask the user to launch the app and use it briefly before running `createzap` a second time. This ensures runtime files (caches, preferences, logs) are created and captured.

**Manual discovery** (if createzap fails):

```bash
# Search common paths
sudo find ~/Library -iname "*<app-name>*" 2>/dev/null
sudo find ~/Library -iname "*<bundle-id>*" 2>/dev/null
```

Common locations:

- `~/Library/Application Support/<app-or-vendor>/`
- `~/Library/Caches/<bundle-id>/`
- `~/Library/Preferences/<bundle-id>.plist`
- `~/Library/Saved Application State/<bundle-id>.savedState/`
- `~/Library/Logs/<app-name>/`
- `~/Library/HTTPStorages/<bundle-id>/`

#### Shared Configuration Directories

Some apps share config directories with related tools (e.g., GUI app + CLI tool). Check if paths like `~/.appname` are used by other casks:

```bash
grep -r "~/\.<appname>" "$(brew --repository homebrew/cask)/Casks/"
```

If shared, add a comment and exclude from zap:

```ruby
zap trash: [
  # "~/.appname", # Shared with appname CLI cask
  "~/Library/Application Support/AppName",
  ...
]
```

## Cask Structure

### Basic Template

```ruby
cask "<token>" do
  version "<version>"
  sha256 "<checksum>"

  url "<download-url>"
  name "<Full App Name>"
  desc "<One-line description>"
  homepage "<homepage-url>"

  livecheck do
    url "<livecheck-url>"
    strategy :<strategy>
  end

  auto_updates true  # if applicable
  depends_on macos: ">= :<minimum-version>"  # if applicable

  app "<App Name>.app"

  zap trash: [
    # paths here
  ]
end
```

### Unversioned Download URLs

When the download URL doesn't contain version info (always points to latest):

```ruby
version "1.2.3"
sha256 :no_check  # URL doesn't include version

url "https://example.com/download/App.dmg",
    verified: "example.com/download/"
```

For Sparkle livecheck with unversioned URLs, use `&:short_version` to return only the display version (not build number):

```ruby
livecheck do
  url "https://example.com/appcast.xml"
  strategy :sparkle, &:short_version
end
```

### Architecture-Specific Versions

When ARM and Intel have different versions:

```ruby
cask "<token>" do
  arch arm: "arm64", intel: "x64"

  on_arm do
    version "<arm-version>"
    sha256 "<arm-checksum>"
  end
  on_intel do
    version "<intel-version>"
    sha256 "<intel-checksum>"
  end

  url "https://example.com/app/#{arch}/App-v#{version}.dmg"
  # ... rest of cask
end
```

### URL Verification

When download domain differs from homepage, add `verified:`:

```ruby
url "https://cdn.example.com/downloads/app.dmg",
    verified: "cdn.example.com/downloads/"
```

## Livecheck Configuration

Choose the appropriate strategy based on how versions are published. See `references/livecheck-strategies.md` in this skill's directory for detailed patterns.

**Quick reference:**

| Source Type               | Strategy         |
| ------------------------- | ---------------- |
| `/latest` redirect URL    | `:header_match`  |
| JSON feed (RELEASES.json) | `:json`          |
| GitHub releases           | `:github_latest` |
| Sparkle appcast           | `:sparkle`       |
| HTML page with links      | `:page_match`    |

## Testing Checklist

### 1. Manual Testing (install/uninstall)

```bash
export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_FROM_API=1

# Test installation - verify app works
brew install --cask <cask-name>

# Test uninstallation
brew uninstall --cask <cask-name>

unset HOMEBREW_NO_AUTO_UPDATE HOMEBREW_NO_INSTALL_FROM_API
```

### 2. Automated Checks (audit, style, livecheck)

Use the `scripts/test-cask` helper in this skill's directory, or run manually:

```bash
export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_FROM_API=1
brew audit --cask --new <cask-name>
brew style --fix <cask-name>
brew livecheck --cask <cask-name>
unset HOMEBREW_NO_AUTO_UPDATE HOMEBREW_NO_INSTALL_FROM_API
```

## PR Submission

### Git Setup

Before pushing, verify remotes to ensure pushing to your fork (not the main Homebrew repo):

```bash
cd "$(brew --repository homebrew/cask)"
git remote -v
```

The main Homebrew repo is typically `origin`. Identify your fork's remote name (e.g., your GitHub username).

### Prepare the Branch

```bash
cd "$(brew --repository homebrew/cask)"
git checkout main
git pull origin main
git checkout -b <cask-name>
git add Casks/<first-letter>/<cask-name>.rb
git commit -m "<cask-name> <version> (new cask)"
```

### Push and Create PR

Push to your fork (not origin):

```bash
git push -u <your-fork-remote> <cask-name>
```

Before creating the PR, read the template at `$(brew --repository homebrew/cask)/.github/PULL_REQUEST_TEMPLATE.md`. Use the template verbatim as the PR body, checking boxes for completed items.

### PR Checklist Verification

Before checking boxes in the PR template, ensure these were actually run:

- `brew audit --cask --online <cask-name>` - Required for all cask changes
- `brew audit --cask --new <cask-name>` - Required for new casks
- `brew style --fix <cask-name>` - Must report no offenses
- `HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <cask-name>` - Must succeed
- `brew uninstall --cask <cask-name>` - Must succeed

### AI Disclosure

The PR template requires AI disclosure. Use this format:

```markdown
## AI Disclosure

I have a skill that automates my standard cask creation workflow: gathering app info
(version, bundle ID, min macOS), checking for auto-update frameworks, finding zap paths,
writing the cask file, and running all the standard tests (audit, style, livecheck,
install/uninstall). I supervised the process throughout.
```

## Common Issues and Solutions

### Different versions per architecture

Use `on_arm`/`on_intel` blocks with arch-specific `version` and `sha256`.

### Livecheck not finding versions

- Debug with `brew livecheck --debug --cask <cask-name>`
- Try different strategies or URLs
- For `/latest` URLs, use `:header_match`
- For JSON feeds, use `:json` with appropriate block
- For unversioned URLs with Sparkle, use `&:short_version`

### Audit failures

- Read error messages carefully - they're usually specific
- `verified:` needed when URL domain != homepage domain
- Use glob `*` instead of specific versions in zap paths (e.g., `sfl*` not `sfl2`)
- "Download does not require additional version components" → use `&:short_version` in Sparkle livecheck

### PR review feedback

Common reviewer requests:

- More specific livecheck strategy
- Additional zap paths
- Better description wording
- Architecture handling improvements

