Homebrew formulae and casks for fmu's tools, plus a few apps that homebrew-cask cannot carry. Most packages are re-pinned to upstream's latest release automatically; the reverse-engineering toolchain is pinned to the versions REA verifies. See Upstream bumps.
# Homebrew 5+ keeps third-party taps untrusted until they are named.
brew trust fmu/tapThen install what you want. Naming a package with the tap also taps this
repository automatically, so brew install fmu/tap/rea works on a machine that
has never tapped it; brew tap fmu/tap alone is enough too, since Homebrew
derives the URL from the tap name:
brew install fmu/tap/rea # the reverse-engineering MCP server and CLI
brew install fmu/tap/rea-full # REA preconfigured with every packaged integration
brew install fmu/tap/unblob # firmware extractor (binwalk, 7z, unar, ...)
brew install fmu/tap/skippy # standalone model-serving CLI (Apple silicon)
brew install fmu/tap/mesh-llm # distributed LLM mesh runtime (Apple silicon)
brew install fmu/tap/jadx-headless-mcp # latest upstream (REA uses the pinned @ keg)
brew install fmu/tap/kuna # latest upstream (REA uses the pinned @ keg)
brew install fmu/tap/ghidra-mcp # standalone Ghidra MCP server (independent of rea)
brew install fmu/tap/serena # semantic code retrieval/editing MCP toolkit (language servers on demand)
brew install fmu/tap/serena-full # serena with pinned OSS language servers
brew install fmu/tap/serena-jetbrains # serena with the paid JetBrains plugin backend
brew install fmu/tap/gbrain fmu/tap/qwen3-embed
brew install fmu/tap/designcraft-cli # the headless DesignCraft engine (matches the app cask)
brew install fmu/tap/figcraft-cli # the headless FigCraft engine (matches the app cask)
brew install fmu/tap/solidcraft-cli # the headless SolidCraft engine (same stream as the app cask)
brew install fmu/tap/vectorcraft-cli # the headless VectorCraft engine (matches the app cask)
brew install fmu/tap/wordcraft-cli # the headless WordCraft engine (matches the app cask)
brew install --cask fmu/tap/baocut fmu/tap/iqualize fmu/tap/mornye fmu/tap/mpv-app
brew install --cask fmu/tap/multica-desktop # links the bundled multica CLI; CLI-only: multica-ai/tap/multicaREA is one CLI and MCP server that reverse
engineers native, managed, Android and JavaScript targets and keeps the
Evidence of each engine separate. This formula installs the published
rea-agents npm package — the same artifact the upstream installer and the
agent plugins use — so rea doctor, rea setup and rea mcp behave exactly
as documented upstream.
brew install fmu/tap/rea
rea doctor --json
rea setup # register agent clientsREA is the reference for the rest of the toolchain: each release verifies one exact release of its engines and reports every other release as unverified, so the tap pins them together with REA instead of tracking each upstream latest:
| package | pinned | REA source of truth |
|---|---|---|
jadx-headless-mcp@0.7.1 |
0.7.1 | src/android/JadxRelease.ts |
kuna@1.833 |
1.833 | src/kuna/KunaRelease.ts |
ghidra@12.1 |
12.1.4 | src/ghidra/GhidraReleasePolicy.ts |
The formula depends on all three, plus node and openjdk. rea finds its
engines through environment variables; set them once (the caveats print the
same block) and rea setup copies the detected values into the agent
registrations it writes:
export GHIDRA_INSTALL_DIR="$(brew --prefix ghidra@12.1)/libexec"
export REA_KUNA_COMMAND="$(brew --prefix kuna@1.833)/bin/kuna"
export REA_JADX_MCP_JAR="$(brew --prefix jadx-headless-mcp@0.7.1)/libexec/jadx-headless-mcp.jar"Hopper is an optional provider and ships as a cask in homebrew-cask:
brew install --cask hopper-disassemblerRizin is the other optional integration this tap can supply
(brew install rizin, then REA_RIZIN_COMMAND); gdb, objdump, apktool,
binwalk, tshark, WABT, sigrok, mitmdump, IDA, JEB and Cutter are detected from
PATH or their own REA_* setting and are not dependencies of this formula.
brew install fmu/tap/rea-full is the alternative that adds the ones Homebrew
packages and the rea/rea-agents launchers that export their settings (see
below); rea doctor --json reports the analysis engines and the agent
registrations it can find, not the optional integrations.
Java note: ghidra@12.1 brings the openjdk@21 that REA's Ghidra bridge is
verified against, while openjdk (latest) serves the JADX bridge. REA picks a
JDK from JAVA_HOME, then PATH, then Homebrew's opt/openjdk; if doctor
complains, export JAVA_HOME for openjdk@21 before rea setup. The
rea-full launcher defaults JAVA_HOME to that release already.
Swift note: REA compiles a small Swift helper with xcrun when it inspects a
provider, so rea doctor needs a Swift compiler; the Command Line Tools
provide one at /usr/bin/swiftc. A nix or direnv shell exports
DEVELOPER_DIR and SDKROOT from nixpkgs' apple-sdk, which carries no Swift
compiler, and that redirect makes the plain CLI fail with
error: tool 'swiftc' not found. The rea-full launchers drop those two
variables when they point into /nix/store (see below); the bare rea formula
has no launcher, so a nix shell there needs unset DEVELOPER_DIR SDKROOT or
brew install rea-full.
rea-full is the one-command way to get REA with every optional integration
Homebrew can install. It depends on rea and on the formulae below, and it
installs the rea/rea-agents launchers that export the matching REA_*
settings, so nothing has to be added to a shell profile:
| integration | formula | REA setting |
|---|---|---|
| debugger and Cutter bridge | rizin |
REA_RIZIN_COMMAND |
| gdb | gdb |
REA_GDB_COMMAND |
| Android APK unpacking | apktool |
REA_APKTOOL_COMMAND |
| firmware images | binwalk |
REA_BINWALK_COMMAND |
| firmware extraction | unblob |
REA_UNBLOB_COMMAND |
| capture dissection | wireshark (tshark, capinfos) |
REA_TSHARK_COMMAND, REA_CAPINFOS_COMMAND |
| WebAssembly | wabt (wasm2wat …) |
REA_WABT_BIN_DIRECTORY |
| signal decoding | sigrok-cli |
REA_SIGROK_COMMAND |
rea itself brings the pinned engines (jadx-headless-mcp@0.7.1,
kuna@1.833 and ghidra@12.1); the standalone ghidra-mcp server is not a REA
provider, so it is not part of the bundle. The launcher supplies their settings
too (GHIDRA_INSTALL_DIR, REA_KUNA_COMMAND, REA_JADX_MCP_JAR and
JAVA_HOME for the verified openjdk@21), so neither the CLI nor the MCP
registrations rea setup writes need exports:
brew install fmu/tap/rea-full
rea doctor --json # finds the pinned Ghidra, Kuna and JADX engines
rea setup # registers this launcher, settings includedThe launcher also drops DEVELOPER_DIR and SDKROOT when they point into
/nix/store. A nix or direnv shell sets them from nixpkgs' apple-sdk, which
has no Swift compiler; xcrun would follow that redirect and REA's Swift helper
compile would fail with error: tool 'swiftc' not found even though the Command
Line Tools provide /usr/bin/swiftc. Only nix-store values are dropped, so a
deliberately selected Xcode keeps working.
An REA_* variable that is already exported overrides the launcher's default,
so pointing REA at another tool keeps working.
rea and rea-full are alternatives, not additions: both provide bin/rea
and bin/rea-agents (the npm package's two command names, which behave
identically), and Homebrew links -full siblings as one family, so installing
one after the other replaces those commands and reports the overwritten links.
The kegs can coexist (rea is rea-full's dependency), but only one of them
owns the commands on PATH. To go back to the plain, unconfigured CLI:
brew uninstall rea-full
brew unlink rea && brew link reaHomebrew leaves the rea keg marked linked even though this launcher replaced
its files, which is why the second line is needed. If Homebrew's autoremove
removed the rea dependency keg together with this formula, brew install fmu/tap/rea brings it (and the engines) back instead.
objdump needs no dependency: the Command Line Tools' /usr/bin/objdump is
what REA looks for by default, and GNU binutils would not do because it is
keg-only and prefixes its tools (gobjdump). The remaining providers cannot be
formula dependencies — Hopper, Cutter and mitmproxy are casks, and IDA and JEB
are not in Homebrew at all:
brew install --cask hopper-disassembler cutter mitmproxyWABT wants an absolute directory rather than a PATH entry, which the launcher
sets; export it only to override it:
export REA_WABT_BIN_DIRECTORY="$(brew --prefix wabt)/bin"rea-full is pinned with rea by scripts/bump/rea.py, so the launcher and
the CLI cannot drift apart.
unblob extracts files from container formats — archive,
compression, filesystem and firmware formats — and is the extractor rea drives
through REA_UNBLOB_COMMAND. It is not in Homebrew, which is why this tap
packages it.
Upstream publishes prebuilt wheels only: unblob itself is a PyO3/maturin
project and several dependencies (lief, pyperscan, cryptography, lz4, …)
are compiled, so the formula installs the published macOS arm64 / CPython 3.14
wheels instead of compiling them. scripts/bump/unblob.py resolves the wheel
set from PyPI and pins every URL and SHA-256; brew upgrade moves the whole set
with the release.
brew install fmu/tap/unblob
unblob --version
unblob --show-external-dependencies # the extractor tools and their statusunblob shells out to external extractor tools. The ones Homebrew packages are
dependencies (p7zip for 7z, unar, lzop, lziprecover, lz4, zstd,
erofs-utils for fsck.erofs, simg2img, partclone for
partclone.restore, sasquatch for SquashFS, and libmagic for the Python
bindings). debugfs comes from the keg-only e2fsprogs formula; the installed
unblob is a wrapper that prepends e2fsprogs' sbin (and Homebrew's own sbin)
to PATH, so every extractor is found out of the box — unblob --show-external-dependencies should list them all with a ✓.
sasquatch is ONEKEY's fork of
squashfs-tools with the vendor SquashFS variants that device firmware uses. It
is not in Homebrew; the formula builds the tagged source (upstream's releases
are Debian packages) and installs the binary under both names unblob invokes:
sasquatch and sasquatch-v4be for big-endian v4 images.
The fork still assumes Linux, so the formula carries a small Darwin patch:
Linux-only headers are guarded, the status thread's sigtimedwait() is
emulated with sigwait(), and the Linux l*xattr() wrappers are mapped onto
Apple's XATTR_NOFOLLOW variants.
brew install fmu/tap/sasquatch
sasquatch -versionpartclone backs up and restores the used blocks of a
partition. unblob drives partclone.restore to unpack partclone images; the
filesystem-specific tools (partclone.extfs, .zfs, .exfat, ...) create
them. The formula builds both, so dumps can be produced on macOS too.
Supported backends: ext2/3/4 (partclone.extfs, via Homebrew e2fsprogs),
NTFS (partclone.ntfs, via this tap's libntfs-3g), FAT12/16/32
(partclone.fat), exFAT (partclone.exfat), HFS+ (partclone.hfsp),
APFS (partclone.apfs), minix (partclone.minix) and ZFS
(partclone.zfs, the upstream 0.3.52 clean-room parser: stripe pool on a single
vdev; Homebrew lz4 plus e2fsprogs' blkid/uuid). The other upstream backends
are not built: XFS/Btrfs/F2FS/reiserfs/reiser4/JFS/NILFS2/UFS/VMFS need
Linux-only libraries or a substantial port.
Upstream targets Linux, so the patch guards linux/fs.h, mntent.h,
malloc.h, linux/types.h and byteswap.h/endian.h, provides a
be16toh/be32toh/be64toh shim over <libkern/OSByteOrder.h>, uses
getmntinfo() for the mount check, reads block-device sizes with
DKIOCGETBLOCK*, maps the Linux-only O_LARGEFILE/O_DIRECT flags to no-ops,
and drops the Linux-only ntfsfixboot helper. The build uses the upstream
autotools flow, with openssl@3, zstd, lz4, keg-only e2fsprogs and
libntfs-3g providing what it links (libext2fs links statically from the keg).
brew install fmu/tap/partclone
partclone.restore --version
partclone.ext4 --version
partclone.ntfs --versionDump an image file and put it back (non-root runs need an explicit -L, as
the default logfile is /var/log/partclone.log; use -W if the restore target
does not exist yet):
partclone.ext4 -c -s disk.img -o disk.partclone.img -L /tmp/partclone.log
partclone.chkimg -s disk.partclone.img -L /tmp/partclone.log
partclone.restore -W -s disk.partclone.img -o restored.img -L /tmp/partclone.logDumps are partclone image format 0002 with the default CRC32 + BIT bitmap;
unblob detects and restores them (it invokes partclone.restore -W ... -L /dev/stdout), so the dumped images can be fed straight back to unblob. The
formula links xxhash, so the optional XXH64/XXH128 checksum modes (-a2/-a3)
are available as well; keep the CRC32 default for unblob-compatible dumps.
ntfs-3g is the
userspace NTFS driver. Its FUSE driver needs macFUSE, which is why Homebrew's
ntfs-3g formula is Linux-only — but partition tools only need the library to
read a volume's allocated clusters. This formula builds libntfs-3g and its
headers alone (no FUSE driver, no ntfsprogs utilities, no crypto) for
partclone.ntfs, and points the dylib's install name at the stable opt path so
dependents keep resolving it across upgrades.
brew install fmu/tap/libntfs-3gntfsprogs is the
userspace utilities half of ntfs-3g: mkntfs, ntfsls, ntfscat, ntfscp,
ntfsclone, ntfsresize, ntfsfix, ntfsinfo, ntfslabel, ntfscmp,
ntfsundelete, ntfscluster, plus the extras ntfswipe, ntfstruncate,
ntfsrecover, ntfsusermap and ntfssecaudit. None of them use the FUSE
driver, so they build on macOS; the formula links libntfs-3g into them
statically and installs no library, so it needs no runtime dependency. The
version is bumped together with libntfs-3g from the same upstream tag.
brew install fmu/tap/ntfsprogs
mkntfs --force --fast --label disk disk.img
ntfsls -l disk.imgSkippy is Mesh-LLM's standalone model-serving product: a CLI that manages models and native runtimes, serves OpenAI- and Anthropic-compatible APIs, and runs explicit split stages without mesh networking.
Upstream publishes a prebuilt skippy CLI for macOS arm64, but only on its
rolling-preview releases — the stable tags do not carry that asset. The formula
therefore pins the newest release that actually ships it, and
scripts/bump/skippy.py follows the same rule. skippy serve installs a
verified native runtime and downloads models on first use.
brew install fmu/tap/skippy
skippy doctor
skippy serve --model Qwen3-0.6B-Q4_K_MMesh-LLM pools GPUs and memory across
machines behind one OpenAI-compatible API at http://localhost:9337/v1. Its
release bundle is a prebuilt mesh-llm host binary plus a verified Metal native
runtime; the formula stages that runtime as an archive and unpacks it after
Homebrew's relocation, because relocation would invalidate the runtime
manifest's per-file hashes.
mesh-llm depends on the skippy formula: Mesh embeds the same serving
implementation, and the standalone CLI is installed for split-stage and runtime
work.
brew install fmu/tap/mesh-llm
mesh-llm setup
mesh-llm serve --autojadx-headless-mcp exposes
JADX's jadx-core as a stdio MCP server for Android APK analysis, with no GUI.
REA drives it through REA_JADX_MCP_JAR, so the formula installs the release
fat JAR at a stable path:
brew install fmu/tap/jadx-headless-mcp # latest upstream release
brew install fmu/tap/jadx-headless-mcp@0.7.1 # the release REA verifies
# REA_JADX_MCP_JAR="$(brew --prefix jadx-headless-mcp@0.7.1)/libexec/jadx-headless-mcp.jar"The unversioned formula tracks upstream's latest release and is bumped by
scripts/bump/jadx-headless-mcp.py. The JAR REA verifies is a separate,
keg-only versioned formula (jadx-headless-mcp@0.7.1) that scripts/bump/rea.py
pins and renames together with REA; REA is pointed at that keg explicitly, so
the latest release never shadows the verified JAR.
Kuna is an agent-first decompiler
distributed as one self-contained executable. REA drives it through
REA_KUNA_COMMAND:
brew install fmu/tap/kuna # latest upstream release
brew install fmu/tap/kuna@1.833 # the release REA verifies
# REA_KUNA_COMMAND="$(brew --prefix kuna@1.833)/bin/kuna"The unversioned formula tracks upstream's latest release and is bumped by
scripts/bump/kuna.py. The release REA verifies is a separate, keg-only
versioned formula (kuna@1.833); only that keg may be on REA's PATH, while
another tap may still ship its own kuna. scripts/bump/rea.py pins and
renames it together with REA: when REA starts verifying a different Kuna, the
script renames the formula (Formula/kuna@<old>.rb → Formula/kuna@<new>.rb,
class KunaAT<old> → KunaAT<new>) and updates the depends_on and
documentation that name it, so the pin is never half-migrated.
GhidraMCP is a standalone Ghidra
MCP server: a Ghidra extension that serves the open program over loopback HTTP,
plus a Python ghidra-mcp MCP server that clients launch. It is independent of
REA, which has its own Ghidra bridge.
brew install fmu/tap/ghidra-mcpThe formula installs the Ghidra extension built for the pinned ghidra@12.1
keg, but Homebrew cannot register Ghidra extensions. Register it once, after
Ghidra has been started at least once (it creates
~/.ghidra/.ghidra_12.1.4_PUBLIC), using either route:
- Ghidra: File > Install Extensions > "+" > select the extension zip in
$(brew --prefix ghidra-mcp)/libexec, or unzip -d ~/.ghidra/.ghidra_12.1.4_PUBLIC/Extensions <that zip>
Restart Ghidra, enable GhidraMCPPlugin under File > Configure > Developer,
then add the server to your MCP client:
{
"mcpServers": {
"ghidra": {
"command": "/opt/homebrew/opt/ghidra-mcp/bin/ghidra-mcp",
"args": ["--ghidra-server", "http://127.0.0.1:8080/"]
}
}
}A release whose extension was built for a different Ghidra version is not
pinned: scripts/bump/ghidra-mcp.py only accepts releases that match the
ghidra@12.1 keg.
GBrain — Postgres-native personal
knowledge brain with hybrid RAG search. The formula installs upstream's
self-contained binary; it is not the unrelated npm gbrain package.
brew install fmu/tap/gbrain
gbrain init --pglite
gbrain doctorUpstream publishes binaries for macOS arm64 and Linux x86_64 only.
The formula does not wrap or disable gbrain's own updater: gbrain upgrade
replaces the binary in the Homebrew keg in place. The scheduled bump keeps the
formula on the latest upstream release, so brew update && brew upgrade gbrain
tracks upstream as well. In-place self-update needs a user-writable Homebrew
prefix.
On-demand local Qwen3 embeddings and reranking, served by
llama.cpp. It runs one loopback
OpenAI-compatible endpoint that fronts two independent llama-server
processes: the embedding model, started on the first embedding request, and
the Qwen3-Reranker-0.6B cross-encoder, started on the first rerank request.
Each one stops again after its own idle window, so either model is resident
only while it is in use and unloading one never disturbs the other.
Nothing in it is specific to gbrain: any
client that speaks the OpenAI embeddings API can use the endpoint, and one
intended use is gbrain embedding (where setup-gbrain also wires up the reranker).
brew install fmu/tap/qwen3-embedThe formula depends on bun and llama.cpp from Homebrew; there are no Nix
dependencies, and serve needs no gbrain. The optional setup-gbrain
integration needs the gbrain CLI on PATH; install it from this same tap
(brew install fmu/tap/gbrain) or use your existing installation.
Post-install, once:
qwen3-embed setup # both GGUFs: ~3.3 GB embedding + ~0.6 GB reranker
brew services start qwen3-embed
qwen3-embed setup-gbrain # optional: configure gbrain (embeddings + reranker) and backfillmodel install reranker installs just the reranker; model install embedding
just the embedder. The files live under
~/.local/share/qwen3-embed/models by default and, with the integration, are
shared by gbrain serve. Both are SHA-256 pinned: the reranker uses the
llama.cpp maintainers' Q8_0 conversion
(ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF), because the community quantized
conversions drop the classifier tensor that makes the scores meaningful.
brew services keeps qwen3-embed serve running at login; qwen3-embed status, qwen3-embed mode throughput, and qwen3-embed bench work against
it (bench --model reranker measures the reranker alone). Upgrading
(brew upgrade qwen3-embed) replaces the code but not the models or
~/.config/qwen3-embed/config.json.
Upgrading from the old gbrain-embed name means reinstalling the formula and
moving the state: brew uninstall gbrain-embed,
mv ~/.local/share/gbrain-embed ~/.local/share/qwen3-embed, same for
~/.local/state/gbrain-embed and ~/.config/gbrain-embed, then
brew install fmu/tap/qwen3-embed.
Uninstall:
brew services stop qwen3-embed
brew uninstall qwen3-embedThe formula tracks the tagged source release of
fmu/qwen3-embed and is bumped by hand
when that repository is tagged. The current pin is v0.4.1: v0.4.0 split
the gbrain integration out of setup into setup-gbrain, and v0.4.1
corrects the tap name in the project README.
Serena is the MCP toolkit that gives a
coding agent IDE-level, symbol-aware retrieval and editing: it runs a language
server per project and exposes the results as MCP tools. The formula installs
the Python application from the tagged source tree, and its Python dependencies
are generated from that release's own uv.lock, so an upgrade installs the
versions upstream tests instead of a fresh resolution.
brew install fmu/tap/serena
serena init
serena setup claude # or: codex; `serena setup --help` lists the clientsThe MCP launcher is serena start-mcp-server; a client that is configured by
hand uses <prefix>/bin/serena start-mcp-server --context <client>. This is the
plain formula: no language server is bundled, uv is a dependency, and SolidLSP
fetches the language server a project needs on first activation (pyright for
Python through uvx, other languages through their own toolchain — node/npm for
TypeScript, a JDK for Java, ...). Serena's state lives under ~/.serena
(override with SERENA_HOME); SolidLSP and uv cache their downloads under
~/.solidlsp and uv's data directory. The optional dashboard's
pywebview/pystray dependencies are installed with the rest.
Two variants pin their code-intelligence backends instead, and install
launchers under the same command names, so the three formulae are alternatives
rather than additions: serena-full pins the open-source
language servers Homebrew packages (plus the ones it does not, from the npm
registry), and serena-jetbrains uses the paid Serena
JetBrains plugin.
scripts/bump/serena.py follows the oraios/serena tags, rewrites the tag and
commit revision, and regenerates the PyPI resource block from the release's
uv.lock; it re-pins the variant formulae too. It fails rather than pinning a
release whose lock no longer supports the formula's python@3.14.
serena-full is the "every packaging-pinned language server" variant,
following the same pattern as rea-full: it depends on serena
and on the language-server formulae, installs the servers Homebrew does not
package into its own keg, and installs serena/serena-agent/serena-hooks
launchers that point SolidLSP at all of them. One brew install then produces a
Serena that downloads no language server on first project activation.
brew install fmu/tap/serena-full
serena init
serena setup claudePinned: pyright (Python), gopls with the Go toolchain, ruby-lsp with Ruby,
rust-analyzer, taplo (TOML), terraform-ls, vscode-html-language-server (HTML),
typescript-language-server with TypeScript (JavaScript too),
some-sass-language-server (SCSS/CSS), @astrojs/language-server and marksman
(Markdown). The launchers export the ls_specific_settings paths through the
environment, so a client registration written by serena setup keeps them
without a shell profile; a setting already present in
~/.serena/serena_config.yml wins over the launcher defaults.
What formula dependencies cannot pin: Rust projects still need a toolchain
(rustup) on PATH, Terraform still needs the terraform CLI (Homebrew stopped
packaging it after the licence change; use hashicorp/tap), and the
experimental HTML, Markdown and SCSS/CSS servers must be enabled per project in
.serena/project.yml (e.g. language_servers: [markdown]). Nix is not
pre-installed because Homebrew packages neither nixd nor nix, and GraphQL is
not supported by Serena 2.1.0 (the documentation on main describes it, but no
GraphQL adapter ships in the release). Like rea-full, serena-full is a
launcher metapackage and is not bottled: it installs in a couple of minutes from
the npm registry.
serena-jetbrains is the variant that uses the paid
Serena JetBrains plugin
as its code-intelligence backend instead of language servers. It depends on
serena, ships the pinned plugin from the JetBrains Marketplace, and installs
serena/serena-agent/serena-hooks launchers that select the backend. It
replaces the scripts/setup-serena-jetbrains.py helper.
brew install fmu/tap/serena-jetbrains
serena-jetbrains # install the pinned IDE and plugin
serena-jetbrains --activate # accept the JetBrains agreement, start the plugin trial
serena init
serena setup claudeserena-jetbrains installs the last Apache-2.0 IntelliJ IDEA Community Edition
build (2025.2.6.4) plus the plugin into the IDE's user plugin directory and the
IDE itself under ~/.serena/jetbrains (override with SERENA_HOME); downloads
are pinned by URL and SHA-256 in the formula, so no marketplace lookup happens
at run time. The IDE is only ever started hidden as a background backend and is
never used as an editor. The first project activation indexes the project and
can take minutes.
The plugin is a paid Marketplace plugin with a 7-day trial: JetBrains only
loads it with an active licence or trial tied to your JetBrains account, which
the formula cannot activate for you. Until it is activated, use the plain
serena formula or serena-full.
All three formulae provide bin/serena, bin/serena-agent and
bin/serena-hooks, so they are alternatives, not additions: installing one
after the other replaces those commands (Homebrew reports the overwritten
links). The serena keg stays installed as the dependency, so swapping back
while a variant is installed is:
brew unlink serena && brew link serenascripts/bump/serena.py re-pins serena-full and serena-jetbrains from the
same release; the IntelliJ and plugin pins are bumped by hand together with
their checksums.
The headless half of DesignCraft,
the page-layout app that is the designcraft cask in homebrew-cask. Upstream
publishes the CLI as a self-contained universal macOS binary next to the app's
own artifacts, so the formula installs that release asset instead of compiling
the engine with Rust: brew install and brew upgrade download it rather than
spending several minutes in cargo install. It runs the app's own layout
composer, renderer and exporter without a window:
brew install fmu/tap/designcraft-cli
designcraft-cli --version
designcraft-cli commands footnote # commands whose id/label/menu match
designcraft-cli run --sample --export page.png # render a page of the built-in sample
designcraft-cli script page.dcs --export page.pdf # batch-edit then export
designcraft-cli mcp # MCP server over stdioThe CLI and the app are built from one upstream tag: the formula was added at
0.15.2, the version the designcraft cask is on, and the scheduled bump moves
it to the latest upstream release — the same release homebrew-cask's autobump
puts the app on. The release binary is Developer ID signed and universal
(arm64 and x86_64), and unlike a source build it embeds the optional extra fonts
from storytold/craft-fonts, so
non-Latin scripts are set in their intended faces instead of falling back to
system fonts. Upstream ships no Linux CLI binary, so the formula is macOS-only.
The headless half of FigCraft, the
interface design and prototyping tool that is the figcraft cask in
homebrew-cask. Upstream publishes the CLI as a self-contained universal macOS
binary next to the app's own artifacts, so the formula installs that release
asset instead of compiling the engine with Rust: brew install and
brew upgrade download it rather than spending several minutes in
cargo install. It runs the app's own document model, renderer and exporters
without a window:
brew install fmu/tap/figcraft-cli
figcraft-cli --version
figcraft-cli commands # every command with its parameters
figcraft-cli info artwork.figcraft # pages, node and style counts
figcraft-cli render artwork.figcraft -o page.png --scale 2
figcraft-cli convert artwork.figcraft artwork.pdf # .figcraft, .json, .svg, .png, .pdf, .fig, .penpot
figcraft-cli run build-card.json --out card.figcraft # a command script, headless
figcraft-cli mcp # MCP server over stdioThe CLI and the app are built from one upstream tag: the formula was added at
0.14.0, the version the figcraft cask is on, and the scheduled bump moves
it to the latest upstream release — the same release homebrew-cask's autobump
puts the app on. The release binary is Developer ID signed, notarized and
universal (arm64 and x86_64), and unlike a source build it embeds the optional
extra fonts from
storytold/craft-fonts, so
non-Latin text is set in its intended faces instead of falling back to system
fonts. Upstream ships no Linux CLI binary, so the formula is macOS-only.
The headless half of SolidCraft, the parametric 3D CAD
modeller that is the solidcraft cask in homebrew-cask. Upstream publishes the
CLI as a self-contained universal macOS binary next to the app's own artifacts,
so the formula installs that release asset instead of compiling the kernel with
Rust: brew install and brew upgrade download it rather than spending several
minutes in cargo install. It runs the app's own solid kernel, evaluator and
exporters without a window:
brew install fmu/tap/solidcraft-cli
solidcraft-cli --version
solidcraft-cli commands # the command registry, as JSON
solidcraft-cli run box.json --out box.step # a command script, exported to STEP
solidcraft-cli eval --in box.step # bodies, mass and volume as JSON
solidcraft-cli snapshot box.json --out box.png # a headless render
solidcraft-cli mcp # MCP server over stdioThe CLI and the app are built from one upstream tag and the scheduled bump
follows the latest upstream release — the same release stream homebrew-cask's
solidcraft cask tracks. The release binary is Developer ID signed, notarized
and universal (arm64 and x86_64). Upstream ships no Linux CLI binary, so the
formula is macOS-only; the archive's deprecated solvecraft-cli alias is not
installed.
The headless half of VectorCraft,
the vector editor that is the vectorcraft cask in homebrew-cask. Upstream
publishes the CLI as a self-contained universal macOS binary next to the app's
own artifacts, so the formula installs that release asset instead of compiling
the engine with Rust: brew install and brew upgrade download it rather than
spending several minutes in cargo install. It runs the app's own document,
rendering and export engine without a window:
brew install fmu/tap/vectorcraft-cli
vectorcraft-cli --version
vectorcraft-cli commands shape.rectangle # the command catalogue, as JSON
vectorcraft-cli info artwork.svg # document summary as JSON
vectorcraft-cli convert artwork.svg artwork.pdf # re-export in another format
vectorcraft-cli run --cmd file.new --cmd shape.rectangle --export page.svg
vectorcraft-cli mcp # MCP server over stdioThe CLI and the app are built from one upstream tag: the formula was added at
0.20.0, the version the vectorcraft cask is on, and the scheduled bump moves
it to the latest upstream release — the same release homebrew-cask's autobump
puts the app on. The release binary is Developer ID signed, notarized and
universal (arm64 and x86_64), and unlike a source build it embeds the optional
extra fonts from
storytold/craft-fonts, so
non-Latin text is set in its intended faces instead of falling back to system
fonts. Upstream ships no Linux CLI binary, so the formula is macOS-only.
The headless half of WordCraft, the
word processor that is the wordcraft cask in homebrew-cask. Upstream publishes
the CLI as a self-contained universal macOS binary next to the app's own
artifacts, so the formula installs that release asset instead of compiling the
engine with Rust: brew install and brew upgrade download it rather than
spending several minutes in cargo install. It runs the app's own document
model, saver and exporters without a window:
brew install fmu/tap/wordcraft-cli
wordcraft-cli --version
wordcraft-cli commands # the command catalogue
wordcraft-cli convert report.docx report.pdf # docx, pdf, odt, rtf, html, md, tex, txt, png
wordcraft-cli text report.docx # plain text
wordcraft-cli inspect report.docx # structure as JSON
wordcraft-cli run --template sample --cmd 'text.insert={"text":"Hello"}' --save out.docx
wordcraft-cli mcp # MCP server over stdioThe CLI and the app are built from one upstream tag: the formula was added at
0.14.0, the version the wordcraft cask is on, and the scheduled bump moves
it to the latest upstream release — the same release homebrew-cask's autobump
puts the app on. The release binary is Developer ID signed, notarized and
universal (arm64 and x86_64), and unlike a source build it embeds the optional
extra fonts from
storytold/craft-fonts, so
non-Latin text is set in its intended faces instead of falling back to system
fonts. Upstream ships no Linux CLI binary, so the formula is macOS-only.
The CLI half of Pared; the app is the
pared-app cask below. Upstream's own formula builds from source and requires
Xcode 27, so this tap pins the released tarball instead. Both artifacts come
from the same tag and are re-pinned together.
brew install fmu/tap/paredThe release is Apple silicon and macOS 27 only, and the formula declares that floor, so installing it on an older host fails instead of producing a binary that cannot start.
Ghidra 12.1.4, pinned. Homebrew core has moved on to 12.2; this keg stays on
12.1.4 because both consumers need that exact release: REA verifies its Ghidra
bridge against 12.1.4, and GhidraMCP publishes one extension per Ghidra release.
A mismatch is not rejected by Ghidra's extension loader at startup — an API move
only shows up later as a NoClassDefFoundError inside the affected tool.
brew install fmu/tap/ghidra@12.1It is keg-only, so it coexists with core's ghidra without either formula
owning /opt/homebrew/bin/ghidraRun. The public release archive ships native
components for Windows and Linux only, so the formula also compiles the macOS
native components (the decompiler, Sleigh and the demangler) with the archive's
documented support/gradle/gradlew buildNatives run. That needs the formula's
JDK 21 and network access for the Gradle wrapper's first download. rea and
ghidra-mcp depend on this keg, so installing either builds it.
The scheduled bump does not touch this formula: the pin is the point. It moves
when REA verifies a new Ghidra release (and GhidraMCP ships an extension for
it), which is a hand change; scripts/bump/rea.py fails rather than publishing
a tap whose REA and Ghidra pins disagree.
None of these casks is in homebrew-cask. baocut, iqualize and
multica-desktop simply have no homebrew-cask entry; mornye, mpv-app and
pared-app fail homebrew-cask's notarization requirement, and their casks carry
the one-time Gatekeeper workaround as a caveat.
BaoCut is an AI video agent with an editable timeline: it transcribes, subtitles, translates, dubs, cuts and animates, and every result lands in a timeline you can keep editing. It is Apple silicon and macOS 14+ only, and upstream signs and notarizes the app, so unlike the other casks here it needs no Gatekeeper workaround. It has no homebrew-cask entry yet, which is why it lives in this tap.
brew install --cask fmu/tap/baocut
open -a BaoCut
baocut-cli version # the app's agent entrypoint, linked into the prefix
baocut-cli status # what this machine can do: runtime, tools and modelsThe app updates itself from the release's appcast feed, so brew upgrade defers
to it; the scheduled bump keeps the tap's pin current for fresh installs.
baocut-cli is the CLI the desktop bundle already ships — the same release as
the app — linked into the Homebrew prefix so it works from a shell;
BAOCUT_CLI can point at $(brew --prefix)/bin/baocut-cli when a tool needs an
absolute path. The cask depends on the ffmpeg formula, which supplies the
ffmpeg and ffprobe the app's media analysis, transcription preparation and
export shell out to.
iQualize is a system-wide parametric EQ: it taps system audio with Core Audio Taps and applies the curve before the audio reaches the output device, so it needs no virtual audio driver. It is Developer ID signed and notarized, so unlike the other apps here it carries no Gatekeeper caveat.
The app installs its own iqualize CLI into /usr/local/bin (Settings >
General > "Install Command Line Tool"), so the cask deliberately adds no
binary stanza: a Homebrew-owned copy would shadow the app's own and drift from
it on upgrade. There is no in-app updater, so brew upgrade is the only route
to a new version.
Mornye is a music library player with synced lyrics and tag editing. The release is ad-hoc signed and not notarized, so Homebrew quarantines it and Gatekeeper blocks the first launch; the caveat says how to approve it once.
The app ships its own updater (auto_updates true), so brew upgrade defers to
it, and downloads its music to ~/Downloads/Mornye — user data that zap
leaves alone. The cask also seeds the community extension registry
(mornyeapp/Mornye-Extension)
into the app's preferences on install and upgrade, so Settings > Extensions >
Repo lists it without the URL being typed in by hand.
The first-party mpv app bundle, built by mpv's own CI and linked from
https://mpv.io/installation/ under "First-party builds". mpv has no notarized
macOS distribution: the mpv formula is CLI-only by design, and the
third-party cask was disabled in homebrew-cask for failing the Gatekeeper check.
The cask tracks the latest stable release, not the rolling git-release
tag: that tag is rebuilt on every push to master and its assets are replaced,
so a pinned cask URL would 404 within a day. It publishes one asset per macOS
version and architecture, and the cask selects the matching one per host.
The cask also links the bundle's own binary as mpv on PATH; the mpv formula
must not be installed next to it. The linked binary sits outside the bundle, so
the bundled Vulkan loader cannot find its MoltenVK ICD from it — launch the app
for vo=gpu-next, or point VK_ICD_FILENAMES at the bundled ICD.
Multica is an agent workspace: you assign an issue to an
AI coding agent the way you would to a teammate, the agent picks it up on a
runtime you control, and the run, comments and result stay attached to the
issue. The desktop app is the native client for that workspace, on top of the
same web UI. Upstream signs and notarizes it, so like baocut and iqualize it
needs no Gatekeeper caveat. It has no homebrew-cask entry yet, which is why it
lives in this tap.
brew install --cask fmu/tap/multica-desktop
open -a Multica
multica version # the CLI copy linked out of the app bundleThe app updates itself with electron-updater, so brew upgrade defers to it;
the scheduled bump keeps the tap's pin current for fresh installs. The bundle
ships a multica CLI from the same release — it is what the app runs for its
managed daemon — and the cask links that copy into the prefix, so multica
works in a terminal and follows the app through its own updates. Upstream also
publishes the CLI as a standalone package in its own tap; that is the route for
a machine that does not want the desktop app, and it owns the same command, so
install one or the other:
brew install multica-ai/tap/multica # CLI onlyThe app keeps its per-service daemon profile under
~/.multica/profiles/desktop-<host>/, next to — but separate from — the
standalone CLI's default profile in ~/.multica/; both packages read the same
directory, but neither reads the other's profile. zap therefore removes only
the files the cask owns.
Pared chooses which Apple Intelligence features
stay on a Mac and removes the downloaded models of the ones that do not. The
app is ad-hoc signed and not notarized, so it lives here with a caveat. It
carries Sparkle (auto_updates true), so brew upgrade defers to the app's own
updater. Like the formula, it is Apple silicon and macOS 27 only.
The bundle contains a copy of the CLI, but the cask has no binary stanza on
purpose: fmu/tap/pared is the CLI's Homebrew route, and a second
Homebrew-owned pared would shadow it.
bump-upstream.yml re-pins the tap's
packages every 30 minutes. For each package, scripts/bump/
resolves upstream's latest release tag, downloads the release asset(s), computes
their SHA-256, rewrites the version and checksums, and — when something moved —
commits, pushes and dispatches the macOS CI run. The commit body lists only the
packages that moved; a package that was already current is reported in the job
log but left out of the commit message. The same holds for the engine pin matrix
scripts/bump/rea.py verifies: its matches REA lines
reach the commit body only when a bump moved rea itself, and a bump that only
touched an unrelated package keeps them in the job log.
The dispatch names the packages the bump moved. That CI run validates only those
packages and the tap formulae that depend on them — computed transitively with
brew uses, so bumping partclone also exercises unblob and rea-full, and
bumping skippy exercises mesh-llm — instead of auditing, installing and
testing the whole tap. Push, pull-request and unscoped manual runs still exercise
everything.
| package | upstream | release tag |
|---|---|---|
rea (+ rea-full, jadx-headless-mcp@<REA pin>, kuna@<REA pin>) |
morluto/rea |
rea-agents-<version> |
jadx-headless-mcp (latest; REA uses jadx-headless-mcp@<REA pin>) |
1013503897/jadx-headless-mcp |
v* |
kuna (latest; REA uses kuna@<REA pin>) |
Noelo-Lab/kuna |
v* |
unblob |
onekey-sec/unblob |
PyPI unblob release (wheels) |
sasquatch |
onekey-sec/sasquatch |
sasquatch-v* |
serena (+ serena-full, serena-jetbrains) |
oraios/serena |
v* (Python resources from the tag's uv.lock) |
partclone |
Thomas-Tsai/partclone |
<version> |
ntfs-3g (libntfs-3g + ntfsprogs) |
tuxera/ntfs-3g |
<version> |
skippy |
Mesh-LLM/mesh-llm |
rolling preview with a skippy CLI asset |
mesh-llm |
Mesh-LLM/mesh-llm |
v* |
ghidra-mcp |
LaurieWired/GhidraMCP |
<version> |
gbrain |
garrytan/gbrain |
v* |
designcraft-cli |
storytold/designcraft |
v* (the tag the designcraft cask is on) |
figcraft-cli |
storytold/figcraft |
v* (the tag the figcraft cask is on) |
solidcraft-cli |
tangency-labs/solidcraft |
v* (the release stream the solidcraft cask tracks) |
vectorcraft-cli |
storytold/vectorcraft |
v* (the tag the vectorcraft cask is on) |
wordcraft-cli |
storytold/wordcraft |
v* (the tag the wordcraft cask is on) |
baocut |
JimLiu/baocut |
baocut-v* |
iqualize |
dariuscorvus/iqualize |
v* |
mornye |
mornyeapp/Mornye-Desktop |
v* |
mpv-app |
mpv-player/mpv |
v* (stable) |
multica-desktop |
multica-ai/multica |
v* |
pared + pared-app |
4evy/pared |
no v prefix |
Two things are coupled rather than independent:
reais the authority for the pinned engine versions.scripts/bump/rea.pypins therea-agentsnpm tarball and reads the engine releases the new tag verifies from its source (src/android/JadxRelease.ts,src/kuna/KunaRelease.ts,src/ghidra/GhidraReleasePolicy.ts), then re-pinsFormula/rea.rb,Formula/rea-full.rband the versioned kegsFormula/jadx-headless-mcp@<release>.rbandFormula/kuna@<release>.rbin one commit. When REA verifies a different JADX or Kuna, the script renames that keg and everydepends_on/doc reference with it. A Ghidra move also needs a newghidra@12.1keg and a GhidraMCP extension built for that release, so the script fails instead of writing a tap whose pins disagree. The unversionedjadx-headless-mcpandkunaformulae are not part of that pin: they track upstream's latest releases and are bumped by their own scripts.ghidra-mcponly accepts a release whose extension targets the Ghidra keg the tap pins; it never creates a mismatched extension.
ghidra@12.1 is deliberately absent from the table (see above), and
qwen3-embed follows a hand-made tag of fmu/qwen3-embed rather than a
GitHub release.
CI also runs python3 scripts/bump/rea.py --check, which compares the tap's
engine pins with the releases the pinned REA version verifies and exits non-zero
on drift without touching the npm registry.
A manual run can pin a specific tag for one package:
gh workflow run bump-upstream.yml -f package=mornye -f tag=v5.2.6
gh workflow run bump-upstream.yml -f package=rea -f tag=rea-agents-6.8.0Bump scripts can also be run locally; they need only python3 and, for the
GitHub API, an optional GH_TOKEN:
python3 scripts/bump/mornye.py # latest upstream release
RELEASE_TAG=v5.2.6 python3 scripts/bump/mornye.py # a specific tag
python3 scripts/bump/kuna.py # latest Kuna, for the unversioned formula
python3 scripts/bump/rea.py --check # verify the engine pin matrixbottles.yml publishes pre-compiled bottles for
the tap's source-built formulae (libntfs-3g, ntfsprogs, partclone,
sasquatch, ghidra@12.1, ghidra-mcp, unblob, serena) as assets of this
repository's bottles GitHub release. brew install uses them automatically, so
the C builds, the pinned Ghidra keg, the Ghidra MCP and serena venvs and the Python
wheel install become downloads instead of local compiles.
Only the current stable macOS is bottled: the workflow pins macos-26, today's
latest stable arm64 image, and Homebrew's older-macOS fallback serves that bottle
on macOS 27 as well. Older macOS releases build from source.
The workflow is dispatched by
bump-upstream.yml when a bump moves one
of them, and by pushes that touch their formulae or patches. It rebuilds all
eight together (partclone links libntfs-3g; ghidra-mcp's extension and venv
target the pinned ghidra@12.1 keg), tests the source builds, bottles them,
uploads the assets, installs the published bottles on a clean runner to check
that they pour, and only then commits the regenerated bottle do blocks. A
manual run:
gh workflow run bottles.ymlThe release is a cache, not an archive: after a successful publish the workflow
prunes every asset that the committed bottle blocks no longer reference, so it
holds only the current bottles. That matters for ghidra@12.1, whose bottle is
about 543 MB (well inside GitHub's 2 GB per-asset limit and the job timeouts).
Bottles are matched by the macOS major they were built on, with a fallback to a
bottle built for an older macOS. A bottle whose cellar is not relocatable is only
poured into the default /opt/homebrew prefix (that is the case for unblob);
other prefixes build from source.
The workflow owns the bottle do blocks. Do not commit a block by hand, and drop
it (the bump scripts do that automatically) if a version or revision bump
changes what the formula installs before the workflow has republished it.
ghidra@12.1 is pinned by hand, so drop its block by hand when the pin moves.
Formulae are under Formula/ and casks under Casks/; both are plain Ruby
files. ci.yml audits every formula and cask on the
latest macOS, installs the packages the runner can run, runs the formula tests,
and checks that the engine pins match the pinned REA release. The two Apple
silicon/macOS 27 artifacts are only installed when the runner is new enough.
An unscoped run (push, pull request, manual dispatch without input) exercises the
whole tap, which installs the reverse-engineering toolchain and builds
ghidra@12.1's macOS native components, dominating the job time. The scheduled
bump workflow instead dispatches the run with the packages it bumped, and CI
validates just those packages and their dependants.
audit_exceptions/mismatched_binary_allowlist.json records the one x86_64
Mach-O helper that Ghidra's own archive carries. It only matters when the keg is
installed locally: audit inspects the installed cellar, and the CI runner audits
before installing.
To validate a change locally before pushing, tap the checkout under a throwaway
name (Homebrew refuses a second tap named fmu/tap while the real one is
installed):
brew tap fmu/audit "file://$PWD" # the tap is named fmu/audit
brew trust fmu/audit
brew audit --strict fmu/audit/gbrain fmu/audit/qwen3-embed \
fmu/audit/pared fmu/audit/ghidra@12.1 \
fmu/audit/jadx-headless-mcp fmu/audit/jadx-headless-mcp@0.7.1 \
fmu/audit/kuna fmu/audit/kuna@1.833 \
fmu/audit/ghidra-mcp fmu/audit/rea \
fmu/audit/skippy fmu/audit/mesh-llm
brew audit --strict --cask fmu/audit/iqualize fmu/audit/mornye \
fmu/audit/mpv-app fmu/audit/multica-desktop fmu/audit/pared-app
brew untap fmu/audit