diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 0b2c848..544bdb3 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -14,3 +14,10 @@ Current environment keys:
Provider integrations are intentionally protocol-only in the repository
foundation phase.
+
+## Packaging
+
+Run `make install-app` to build the release executable, package it as
+`AI Music Studio.app`, install it in `/Applications`, and launch it.
+
+Set `APP_ICON=/path/to/icon.icns` when packaging with a custom icon.
diff --git a/Makefile b/Makefile
index a6e0f26..19ea76c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: build dev test
+.PHONY: build dev test install-app
build:
swift build
@@ -8,3 +8,6 @@ dev:
test:
swift test
+
+install-app:
+ scripts/build-install-launch.zsh
diff --git a/scripts/build-install-launch.zsh b/scripts/build-install-launch.zsh
new file mode 100755
index 0000000..b30d0b1
--- /dev/null
+++ b/scripts/build-install-launch.zsh
@@ -0,0 +1,156 @@
+#!/bin/zsh
+# Build, package, install, and launch the AI Music Studio macOS application.
+
+set -euo pipefail
+
+PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+APP_DISPLAY_NAME="${APP_DISPLAY_NAME:-AI Music Studio}"
+EXECUTABLE_NAME="${EXECUTABLE_NAME:-music-assistant}"
+BUNDLE_IDENTIFIER="${BUNDLE_IDENTIFIER:-com.musicassistant.app}"
+APP_BUNDLE_NAME="${APP_DISPLAY_NAME}.app"
+BUILD_DIR="${PROJECT_ROOT}/.build/app"
+APP_BUNDLE="${BUILD_DIR}/${APP_BUNDLE_NAME}"
+INSTALL_DIR="${INSTALL_DIR:-/Applications}"
+INSTALL_PATH="${INSTALL_DIR}/${APP_BUNDLE_NAME}"
+CONTENTS_DIR="${APP_BUNDLE}/Contents"
+MACOS_DIR="${CONTENTS_DIR}/MacOS"
+RESOURCES_DIR="${CONTENTS_DIR}/Resources"
+
+fail() {
+ print -u2 -- "Error: $1"
+ exit 1
+}
+
+step() {
+ print -- "==> $1"
+}
+
+find_app_icon() {
+ if [[ -n "${APP_ICON:-}" ]]; then
+ [[ -f "${APP_ICON}" ]] || fail "App icon was not found at ${APP_ICON}."
+ print -- "${APP_ICON}"
+ return
+ fi
+
+ local candidate
+ for candidate in \
+ "${PROJECT_ROOT}/Resources/AI Music Studio.icns" \
+ "${PROJECT_ROOT}/Resources/AppIcon.icns" \
+ "${PROJECT_ROOT}/Resources/MusicAssistant.icns"; do
+ if [[ -f "${candidate}" ]]; then
+ print -- "${candidate}"
+ return
+ fi
+ done
+}
+
+[[ -f "${PROJECT_ROOT}/Package.swift" ]] || fail "Package.swift was not found in ${PROJECT_ROOT}."
+command -v swift >/dev/null 2>&1 || fail "Swift is required. Install Xcode or the Xcode Command Line Tools."
+command -v codesign >/dev/null 2>&1 || fail "codesign is required and should be available with Xcode."
+command -v ditto >/dev/null 2>&1 || fail "ditto is required and should be available on macOS."
+command -v open >/dev/null 2>&1 || fail "open is required and should be available on macOS."
+
+cd "${PROJECT_ROOT}"
+
+step "Stopping any running ${APP_DISPLAY_NAME} application"
+if pgrep -x "${EXECUTABLE_NAME}" >/dev/null 2>&1; then
+ pkill -x "${EXECUTABLE_NAME}" || fail "Could not stop the running ${APP_DISPLAY_NAME} application."
+ for _ in {1..20}; do
+ pgrep -x "${EXECUTABLE_NAME}" >/dev/null 2>&1 || break
+ sleep 0.1
+ done
+ pgrep -x "${EXECUTABLE_NAME}" >/dev/null 2>&1 && fail "${APP_DISPLAY_NAME} is still running. Quit it and retry."
+fi
+
+step "Removing stale installed copy"
+if [[ -e "${INSTALL_PATH}" ]]; then
+ rm -rf "${INSTALL_PATH}" || fail "Could not remove ${INSTALL_PATH}. Check permissions and retry."
+fi
+
+step "Cleaning local Swift build artifacts"
+swift package clean || fail "Swift Package Manager could not clean the local build artifacts."
+
+step "Building ${EXECUTABLE_NAME} in release mode"
+BIN_PATH="$(swift build -c release --show-bin-path 2>/dev/null | tail -n1)"
+SOURCE_EXECUTABLE="${BIN_PATH}/${EXECUTABLE_NAME}"
+
+built=0
+for attempt in 1 2 3; do
+ swift build -c release || fail "Release build failed. Review the Swift compiler diagnostics above."
+ if [[ -x "${SOURCE_EXECUTABLE}" ]]; then
+ built=1
+ break
+ fi
+ print -- "==> Executable not present after build (attempt ${attempt}); retrying"
+ sleep 1
+done
+[[ "${built}" -eq 1 ]] || fail "Built executable was not found at ${SOURCE_EXECUTABLE}."
+
+step "Packaging ${APP_BUNDLE_NAME}"
+rm -rf "${APP_BUNDLE}"
+mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
+cp "${SOURCE_EXECUTABLE}" "${MACOS_DIR}/${EXECUTABLE_NAME}"
+
+ICON_PATH="$(find_app_icon)"
+PLIST_ICON_ENTRY=""
+if [[ -n "${ICON_PATH}" ]]; then
+ ICON_FILE_NAME="$(basename "${ICON_PATH}")"
+ ICON_FILE_BASE="${ICON_FILE_NAME:r}"
+ cp "${ICON_PATH}" "${RESOURCES_DIR}/${ICON_FILE_NAME}"
+ PLIST_ICON_ENTRY=$' CFBundleIconFile\n '"${ICON_FILE_BASE}"$''
+else
+ step "No app icon found; packaging without a custom icon"
+fi
+
+cat > "${CONTENTS_DIR}/Info.plist" <
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${APP_DISPLAY_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIdentifier
+ ${BUNDLE_IDENTIFIER}
+ CFBundleInfoDictionaryVersion
+ 6.0
+${PLIST_ICON_ENTRY}
+ CFBundleName
+ ${APP_DISPLAY_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ 14.0
+ NSHighResolutionCapable
+
+ NSPrincipalClass
+ NSApplication
+
+
+PLIST
+
+plutil -lint "${CONTENTS_DIR}/Info.plist" >/dev/null || fail "Generated Info.plist is invalid."
+codesign --force --sign - --timestamp=none "${APP_BUNDLE}" || fail "Ad-hoc signing of ${APP_BUNDLE_NAME} failed."
+codesign --verify --deep --strict "${APP_BUNDLE}" || fail "Packaged ${APP_BUNDLE_NAME} failed code-signature verification."
+[[ -x "${MACOS_DIR}/${EXECUTABLE_NAME}" ]] || fail "Packaged executable is missing."
+
+step "Installing ${APP_BUNDLE_NAME} in ${INSTALL_DIR}"
+ditto "${APP_BUNDLE}" "${INSTALL_PATH}" || fail "Could not install ${APP_BUNDLE_NAME} in ${INSTALL_DIR}. Check permissions and retry."
+
+INSTALLED_EXECUTABLE="${INSTALL_PATH}/Contents/MacOS/${EXECUTABLE_NAME}"
+[[ -x "${INSTALLED_EXECUTABLE}" ]] || fail "Installed executable is missing at ${INSTALLED_EXECUTABLE}."
+SOURCE_HASH="$(shasum -a 256 "${MACOS_DIR}/${EXECUTABLE_NAME}" | awk '{print $1}')"
+INSTALLED_HASH="$(shasum -a 256 "${INSTALLED_EXECUTABLE}" | awk '{print $1}')"
+[[ "${SOURCE_HASH}" == "${INSTALLED_HASH}" ]] || fail "Installed executable does not match the newly built executable."
+
+step "Launching ${APP_DISPLAY_NAME}"
+open -n "${INSTALL_PATH}" || fail "${APP_DISPLAY_NAME} was installed but could not be opened."
+
+print -- "==> ${APP_DISPLAY_NAME} was rebuilt, installed, and launched successfully."