The macOS app ships craft-anything-cli beside its executable

The DMG's app had only craft-anything, so the app couldn't start its MCP server and every Claude Code build failed with no tools (#91, #96). The CLI is now copied into Contents/MacOS, signed like its zip, and run from the finished DMG before release.
This commit is contained in:
bflatastic
2026-11-01 22:42:14 +07:00
parent 348949ca76
commit 2a8e24f144
3 changed files with 19 additions and 2 deletions
+4
View File
@@ -115,6 +115,10 @@ after a live run check that the command returned and nothing it started is still
was copied as `cli-r25.exe`; the CLI finds itself by name (`craft-anything-cli`) to start the
builder's MCP server, so Claude Code saw no tools and stopped. Copy it under its own name into a
folder of its own.
It happened again in a release: the macOS app bundle had only `craft-anything`, with the CLI in
a separate zip, so every Claude Code build from the 0.2.0 DMG failed the same way (#91). Every
package that ships the app ships the CLI beside its executable, and the packaging script runs
the CLI from the finished package before it's published.
**A Windows path with forward slashes read as nothing.** rtl_433 decoded a recording in the shell
but found no messages, and exited 0, when the test passed `C:/…/x.cu8`; it only reads Windows
+2 -1
View File
@@ -41,7 +41,8 @@ Plus `SHA256SUMS.txt`.
scheme on any platform. It appears in Windows' "Open with" list and accepts paths on the
command line. Projects (`*.cany`) are folders.
- **The macOS executable** is `Craft Anything.app/Contents/MacOS/craft-anything`. Only the bundle
carries the display name.
carries the display name. `craft-anything-cli` sits beside it, as it does in the Windows zip: the
app starts it as the coding agents' MCP server, so an app without it can't build (#91).
- **Windows portable mode:** the portable zip ships `portable.txt`. While it sits next to the exe,
settings live in `CraftAnythingData\` (`crates/engine/src/settings.rs`).
- **Flatpak** grants `--share=network` (AI backends, engine downloads) and `--filesystem=home:ro`
+13 -1
View File
@@ -106,6 +106,9 @@ echo "==> assembling $APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
# The executable keeps the machine name (CFBundleExecutable); the bundle carries the display name.
cp "$WORK/bin/craft-anything" "$APP/Contents/MacOS/craft-anything"
# The CLI goes beside it: the app starts it as the MCP server its coding agents use, and finds it
# next to its own executable (`self_cli_path`). Without it every Claude Code build fails (#91).
if [ "$HAS_CLI" = 1 ]; then cp "$WORK/bin/craft-anything-cli" "$APP/Contents/MacOS/craft-anything-cli"; fi
cp "$ICNS" "$APP/Contents/Resources/craft-anything.icns"
# Licences of the embedded fonts (assets/fonts, OFL-1.1).
mkdir -p "$APP/Contents/Resources/Licenses"
@@ -117,7 +120,11 @@ plutil -lint "$APP/Contents/Info.plist"
printf 'APPL????' >"$APP/Contents/PkgInfo"
# Sign inside-out: nested code first, then the bundle itself (no --deep on the final signature).
# Today the only nested code is the main executable; frameworks/helpers would be signed here too.
# The nested code is the main executable and the CLI; frameworks/helpers would be signed here too.
# The CLI keeps the identifier its zip has.
if [ "$HAS_CLI" = 1 ]; then
sign --options runtime --identifier ai.storyteller.craft-anything-cli "$APP/Contents/MacOS/craft-anything-cli"
fi
sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP/Contents/MacOS/craft-anything"
sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP"
codesign --verify --strict --deep --verbose=2 "$APP"
@@ -172,6 +179,11 @@ if ! codesign --verify --strict --deep --verbose=2 "$MOUNT/Craft Anything.app";
hdiutil_retry detach detach "$MOUNT" >/dev/null || true
exit 1
fi
if [ "$HAS_CLI" = 1 ] && ! "$MOUNT/Craft Anything.app/Contents/MacOS/craft-anything-cli" --version; then
echo "error: the app in the DMG has no working craft-anything-cli beside its executable" >&2
hdiutil_retry detach detach "$MOUNT" >/dev/null || true
exit 1
fi
hdiutil_retry detach detach "$MOUNT" >/dev/null
if [ "$NOTARIZE" = 1 ]; then
notarize "$DMG"