App Store Deploy
romankurnovskii/BrewMate
Automates the build and deployment of the Electron app to the Mac App Store and TestFlight.
A skill your agent uses when promoting afm to a stable release — builds from main HEAD or a nightly commit, verifies patches, updates Homebrew stable tap (afm.rb), builds a PyPI wheel, updates…
The automated check flagged lines worth reading first. See the safety section below.
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightly --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .claude/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .claude/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightlyType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightly --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .agents/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .agents/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightly --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .cursor/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .cursor/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/scouzi1966/maclocal-api.git --path .claude/skills/afm-build-promote-nightly--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightly --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .gemini/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .gemini/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightlyInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .github/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .github/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install scouzi1966/maclocal-api afm-build-promote-nightly --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/scouzi1966/maclocal-api.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/afm-build-promote-nightly .opencode/skills/afm-build-promote-nightly && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "afm-build-promote-nightly" agent skill from https://github.com/scouzi1966/maclocal-api/tree/main/.claude/skills/afm-build-promote-nightly into .opencode/skills/afm-build-promote-nightly/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "afm-build-promote-nightly", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
afm-build-promote-nightlyA skill your agent uses when promoting afm to a stable release — builds from main HEAD or a nightly commit, verifies patches, updates Homebrew stable tap (afm.rb), builds a PyPI wheel, updates…
Afm Build Promote Nightly is an agent skill from scouzi1966/maclocal-api. Use when promoting afm to a stable release — builds from main HEAD or a nightly commit, verifies patches, updates Homebrew stable tap (afm.rb), builds a PyPI wheel, updates README and version files, and verifies both brew install and pip install work. Repo admin only.
Its SKILL.md is about 8.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Mobile, covering App store release. It works with Homebrew and macOS. The repository describes itself as: 'afm' command cli: macOS server and single prompt mode that exposes Apple's Foundation and MLX Models and other APIs running on your Mac through a single aggregated…. The licence is MIT.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 138ca5d. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitbrewghpipswiftuvnpmrubycurlpython3From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
test.pypi.orgFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
UV_PUBLISH_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Afm Build Promote Nightly loads about 8.9k tokens when it runs. Until then it costs about 74 tokens; SKILL.md has 1,963 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found patterns that need a careful read before installing.
me (`--token` / `UV_PUBLISH_TOKEN` / `~/.pypirc`) — never assume it's stored in the repo.Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from scouzi1966/maclocal-api at commit 138ca5d, republished under its MIT licence (© scouzi1966). 1,963 words, ~8,866 tokens.
.claude/skills/afm-build-promote-nightly/SKILL.md (or your agent's skills folder).Build a stable release from either the current main branch HEAD or a specific nightly commit. Updates BuildInfo.swift with the stable version, rebuilds with verified patches, then publishes to GitHub, Homebrew (afm.rb), and PyPI.
Repo admin only (scouzi1966).
/afm-build-promote-nightly — build stable release (choose source interactively)/afm-build-promote-nightly 0.9.7 — build stable with explicit versiongh auth status
GH_USER=$(gh api user -q .login)
echo "GitHub user: $GH_USER"
gh api repos/scouzi1966/maclocal-api -q '.permissions.push'
gh api repos/scouzi1966/homebrew-afm -q '.permissions.push'
# Tap repo — auto-clone if missing
TAP_DIR="${TAP_DIR:-$(cd "$(git rev-parse --show-toplevel)/.." && pwd)/homebrew-afm}"
if [ ! -d "$TAP_DIR/.git" ]; then
echo "Tap repo missing — cloning..."
gh repo clone scouzi1966/homebrew-afm "$TAP_DIR"
fi
test -f "$TAP_DIR/afm.rb" && echo "Tap OK" || echo "FAILED"If GH_USER is not scouzi1966 or push access is false for either repo, STOP immediately:
This skill publishes stable releases for scouzi1966/maclocal-api.
Only the repository owner (scouzi1966) can run it. You are authenticated as: <username>List recent nightly releases for reference:
# Show current main HEAD
echo "main HEAD: $(git rev-parse --short HEAD) — $(git log -1 --format='%s')"
# List recent nightlies
gh release list --repo scouzi1966/maclocal-api --limit 10 --json tagName,name,publishedAt,isPrerelease \
-q '.[] | select(.isPrerelease) | "\(.tagName)\t\(.name)\t\(.publishedAt)"'Use AskUserQuestion:
Question: "Build stable release from which source?"
Options:
If "main HEAD": Set BUILD_SHA=$(git rev-parse HEAD)
If "A nightly release": Show the nightly list and ask user to pick one, then:
NIGHTLY_TAG="<selected tag>"
BUILD_SHA=$(git rev-list -n 1 "$NIGHTLY_TAG" 2>/dev/null || \
gh release view "$NIGHTLY_TAG" --repo scouzi1966/maclocal-api --json targetCommitish -q '.targetCommitish')If "A specific commit": User provides the SHA, set BUILD_SHA=<user input>
echo "Build source commit: $BUILD_SHA"
echo "Commit message: $(git log -1 --format='%s' $BUILD_SHA)"If version was provided as argument, use it. Otherwise derive from BuildInfo.swift:
BASE_VERSION=$(grep 'static let version' Sources/AFMKit/BuildInfo.swift \
| sed 's/.*"\(.*\)".*/\1/' | sed 's/^v//')Use AskUserQuestion:
Question: "Stable version? Derived base version is X.Y.Z."
Options:
X.Y.Z (derived)" — use as-isSave as VERSION. Set TAG="v${VERSION}".
Check stable tag doesn't already exist:
if gh release view "v${VERSION}" --repo scouzi1966/maclocal-api >/dev/null 2>&1; then
echo "ERROR: Release v${VERSION} already exists!"
fiIf it exists, STOP and ask the user to choose a different version.
Before any mutations, snapshot the current state so the entire promotion can be reverted (except PyPI, which is immutable).
cd "$(git rev-parse --show-toplevel)"
# Previous stable version (for tap rollback)
PREV_STABLE_VERSION=$(grep 'version "' "$TAP_DIR/afm.rb" | head -1 | sed 's/.*"\(.*\)".*/\1/')
PREV_STABLE_URL=$(grep 'url "' "$TAP_DIR/afm.rb" | head -1 | sed 's/.*"\(.*\)".*/\1/')
PREV_STABLE_SHA256=$(grep 'sha256 "' "$TAP_DIR/afm.rb" | head -1 | sed 's/.*"\(.*\)".*/\1/')
# Previous version file values
PREV_BUILDINFO_VERSION=$(grep 'static let version' Sources/AFMKit/BuildInfo.swift | sed 's/.*"\(.*\)".*/\1/')
PREV_PYPROJECT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/.*"\(.*\)".*/\1/')
PREV_INIT_VERSION=$(grep '^__version__' macafm/__init__.py | sed 's/.*"\(.*\)".*/\1/')
# Previous README version
PREV_README_STABLE=$(grep -o 'Stable (v[^)]*)' README.md | head -1)
# Tap commit before our changes (production)
PREV_TAP_COMMIT=$(cd "$TAP_DIR" && git rev-parse HEAD)
# Staging tap commit before our changes (used in Step 8b)
STAGING_DIR="${STAGING_TAP_DIR:-$(cd "$(git rev-parse --show-toplevel)/.." && pwd)/homebrew-afm-staging}"
if [ -d "$STAGING_DIR/.git" ]; then
PREV_STAGING_COMMIT=$(cd "$STAGING_DIR" && git rev-parse HEAD)
else
PREV_STAGING_COMMIT="(repo not yet cloned — will be created in Step 8b)"
fi
echo "=== Rollback snapshot ==="
echo "Tap version: $PREV_STABLE_VERSION"
echo "Tap commit: $PREV_TAP_COMMIT"
echo "Staging commit: $PREV_STAGING_COMMIT"
echo "BuildInfo: $PREV_BUILDINFO_VERSION"
echo "pyproject: $PREV_PYPROJECT_VERSION"
echo "__init__: $PREV_INIT_VERSION"
echo "README: $PREV_README_STABLE"Report the rollback snapshot to the user so they have it on record.
CRITICAL: The stable build must use the chosen commit (BUILD_SHA), with only BuildInfo.swift changed to carry the stable version.
cd "$(git rev-parse --show-toplevel)"
# Save current branch to return later
ORIGINAL_BRANCH=$(git branch --show-current)
# Stash any uncommitted changes
git stash --include-untracked 2>/dev/null || true
# Checkout the chosen commit
git checkout "$BUILD_SHA"
# Update ONLY BuildInfo.swift with stable version
sed -i '' "s/static let version: String? = \".*\"/static let version: String? = \"v${VERSION}\"/" \
Sources/AFMKit/BuildInfo.swift
# Verify the change
grep 'static let version' Sources/AFMKit/BuildInfo.swift
# Must show: static let version: String? = "vX.Y.Z"CRITICAL FAIL-SAFE: Before building, verify that ALL vendor patches are correctly applied. The build MUST use our patched files, not upstream originals.
cd "$(git rev-parse --show-toplevel)"
# Step 5a: Initialize submodules at the exact state for this commit
git submodule update --init --recursive
# Step 5b: Apply patches
./Scripts/apply-mlx-patches.sh
# Step 5c: FAIL-SAFE — Verify every patch file matches its vendor target
./Scripts/apply-mlx-patches.sh --check
PATCH_CHECK=$?
if [ $PATCH_CHECK -ne 0 ]; then
echo "FATAL: Patch verification FAILED. Aborting build."
echo "One or more vendor files do not match Scripts/patches/."
echo "This means the build would use unpatched vendor code."
fiIf --check exits non-zero, STOP IMMEDIATELY. Do NOT proceed with the build. Report the failing patches to the user and investigate.
Additional per-file verification — confirm critical patched files byte-for-byte:
# Verify the most critical patches individually
PATCHES_DIR="$(git rev-parse --show-toplevel)/Scripts/patches"
VENDOR_DIR="$(git rev-parse --show-toplevel)/vendor/mlx-swift-lm"
CRITICAL_PATCHES=(
"Evaluate.swift:Libraries/MLXLMCommon/Evaluate.swift"
"LLMModelFactory.swift:Libraries/MLXLLM/LLMModelFactory.swift"
"ToolCallFormat.swift:Libraries/MLXLMCommon/Tool/ToolCallFormat.swift"
"Load.swift:Libraries/MLXLMCommon/Load.swift"
"KVCache.swift:Libraries/MLXLMCommon/KVCache.swift"
)
ALL_OK=true
for entry in "${CRITICAL_PATCHES[@]}"; do
PATCH_NAME="${entry%%:*}"
TARGET_REL="${entry##*:}"
if ! diff -q "$PATCHES_DIR/$PATCH_NAME" "$VENDOR_DIR/$TARGET_REL" >/dev/null 2>&1; then
echo "MISMATCH: $PATCH_NAME != $TARGET_REL"
ALL_OK=false
else
echo "OK: $PATCH_NAME"
fi
done
if [ "$ALL_OK" = false ]; then
echo "FATAL: Critical patch mismatch detected. Aborting."
fiIf any critical patch fails diff, STOP. Do not build.
Step 5d: Build WebUI assets (MANDATORY):
The WebUI (-w flag) is a core feature. Every release MUST include Resources/webui/index.html.gz. This asset comes from the llama.cpp submodule and must be built or copied before packaging.
cd "$(git rev-parse --show-toplevel)"
# Build webui from llama.cpp submodule
WEBUI_DIR="vendor/llama.cpp/tools/server/webui"
WEBUI_PREBUILT="vendor/llama.cpp/tools/server/public/index.html.gz"
if [ -f "$WEBUI_PREBUILT" ]; then
# Pre-built webui exists in submodule — copy it
mkdir -p Resources/webui
cp "$WEBUI_PREBUILT" Resources/webui/index.html.gz
echo "WebUI: copied pre-built from llama.cpp submodule"
elif [ -d "$WEBUI_DIR" ] && command -v node >/dev/null 2>&1; then
# Build from source
cd "$WEBUI_DIR" && npm install && npm run build && cd "$(git rev-parse --show-toplevel)"
mkdir -p Resources/webui
cp vendor/llama.cpp/tools/server/public/index.html.gz Resources/webui/index.html.gz
echo "WebUI: built from source"
else
echo "FATAL: Cannot build or find WebUI assets."
echo "Neither $WEBUI_PREBUILT nor $WEBUI_DIR with Node.js available."
fi
# MANDATORY CHECK — release MUST have webui
if [ ! -f "Resources/webui/index.html.gz" ]; then
echo "FATAL: Resources/webui/index.html.gz is MISSING. Cannot release without WebUI."
else
echo "WebUI OK: $(ls -lh Resources/webui/index.html.gz | awk '{print $5}')"
fiIf Resources/webui/index.html.gz does not exist, STOP IMMEDIATELY. Do NOT proceed with the build. The WebUI is a core feature and every release must include it.
Step 5e: Build for release:
# Clean and build release
swift package clean
swift build -c releaseStep 5f: Post-build version verification:
BIN=".build/arm64-apple-macosx/release/afm"
[ -x "$BIN" ] || BIN=".build/release/afm"
# The binary MUST report the stable version
REPORTED_VERSION=$($BIN --version 2>&1)
echo "Binary reports: $REPORTED_VERSION"
if ! echo "$REPORTED_VERSION" | grep -qF "v${VERSION}"; then
echo "FATAL: Binary reports '$REPORTED_VERSION' but expected 'v${VERSION}'"
echo "BuildInfo.swift was not picked up by the build."
fiIf the binary version doesn't match v${VERSION}, STOP. The build did not incorporate the version change.
Step 5g: Relocated binary check (MANDATORY — blocks pip/Homebrew install crash):
SPM auto-generates resource_bundle_accessor.swift with a hardcoded absolute build path. If any code calls Bundle.module, the binary will fatalError when relocated (pip install, Homebrew). This has shipped broken releases before. This check is non-negotiable.
# 1. Source code audit — Bundle.module must NEVER appear in non-comment source
HITS=$(grep -r 'Bundle\.module' Sources/ --include='*.swift' | grep -v '^\s*//' | grep -v '// ' | wc -l | tr -d ' ')
if [ "$HITS" -gt 0 ]; then
echo "FATAL: Found $HITS Bundle.module call(s) — will crash on pip/Homebrew install"
grep -rn 'Bundle\.module' Sources/ --include='*.swift' | grep -v '//'
echo "STOP. Remove all Bundle.module calls before proceeding."
exit 1
fi
echo "PASS: No Bundle.module calls in source"
# 2. Runtime simulation — copy binary + loose metallib to temp dir (NO SPM bundle)
TMPDIR=$(mktemp -d)
cp "$BIN" "$TMPDIR/"
cp "$(dirname "$BIN")/AFMKit_AFMKitMLX.bundle/default.metallib" "$TMPDIR/"
MACAFM_MLX_MODEL_CACHE=/Volumes/edata/models/vesta-test-cache \
"$TMPDIR/afm" mlx -m mlx-community/Qwen3.5-35B-A3B-4bit -s "hello" --max-tokens 5 2>&1 | head -3
EXIT_CODE=${PIPESTATUS[0]}
rm -rf "$TMPDIR"
if [ "$EXIT_CODE" -ne 0 ]; then
echo "FATAL: Relocated binary crashed (exit $EXIT_CODE)"
echo "This means pip install and Homebrew install will crash on first run."
echo "STOP. Fix MLXMetalLibrary.swift before proceeding."
exit 1
fi
echo "PASS: Relocated binary runs without crash"If either check fails, STOP IMMEDIATELY. Do NOT package, publish, or release. Every pip and Homebrew user will get a crash on first run.
Step 5h: Info.plist embedding check (MANDATORY — blocks Speech Recognition SIGABRT):
macOS 26 SIGABRTs any process that requests privacy-sensitive APIs (Speech Recognition, microphone, camera, etc.) without the matching *UsageDescription key in its embedded Info.plist. Required for afm speech, POST /v1/audio/transcriptions, and chat input_audio content parts. Any future privacy-API integration needs its *UsageDescription key added here too.
# 1. Verify __TEXT,__info_plist section is present in the binary
if ! otool -l "$BIN" | grep -q '__info_plist'; then
echo "FATAL: Missing __TEXT,__info_plist section in binary"
echo "Check Package.swift linker flags (-Xlinker -sectcreate ...) and Sources/AFMCLI/Info.plist"
exit 1
fi
# 2. Verify NSSpeechRecognitionUsageDescription key is embedded
if ! strings "$BIN" | grep -q 'NSSpeechRecognitionUsageDescription'; then
echo "FATAL: NSSpeechRecognitionUsageDescription missing from embedded plist"
echo "afm speech / /v1/audio/transcriptions will SIGABRT on macOS 26"
exit 1
fi
# 3. Verify Info.plist source file is well-formed
plutil -lint Sources/AFMCLI/Info.plist || { echo "FATAL: Info.plist is malformed"; exit 1; }
# 4. Verify Apple frameworks that require the embedded plist are actually linked.
# PR #104 (Vision OCR) and PR #107 (Speech transcription) depend on these — if they
# drop out of Package.swift the plist is pointless because the APIs can't be called.
REQUIRED_FRAMEWORKS=(Speech Vision PDFKit ImageIO)
FW_MISSING=()
for FW in "${REQUIRED_FRAMEWORKS[@]}"; do
if ! otool -L "$BIN" | grep -q "/${FW}.framework/"; then
FW_MISSING+=("$FW")
fi
done
if [ ${#FW_MISSING[@]} -gt 0 ]; then
echo "FATAL: Binary missing required framework links: ${FW_MISSING[*]}"
echo "Check Package.swift .linkedFramework(...) entries — PR #104/#107 features will not work."
exit 1
fi
# 5. Verify CFBundleIdentifier (TCC identity) is embedded. Changing this bundle id
# forces every user to re-grant Speech/mic/camera permission — treat as a contract.
if ! strings "$BIN" | grep -q 'com.scouzi1966.afm'; then
echo "FATAL: Embedded plist missing CFBundleIdentifier=com.scouzi1966.afm (TCC identity broken)"
exit 1
fi
echo "PASS: Info.plist embedded with NSSpeechRecognitionUsageDescription"
echo "PASS: Required frameworks linked: ${REQUIRED_FRAMEWORKS[*]}"
echo "PASS: CFBundleIdentifier present in embedded plist"If this fails, STOP. Shipping a stable release with broken Speech Recognition is worse than shipping a nightly — stable users have higher expectations.
Note on CFBundleIdentifier in Info.plist: The CFBundleIdentifier (com.scouzi1966.afm) establishes the TCC identity. Changing it later forces every user to re-grant Speech Recognition / microphone / camera permission. Treat it as a stable contract across releases.
cd "$(git rev-parse --show-toplevel)"
BIN=".build/arm64-apple-macosx/release/afm"
[ -x "$BIN" ] || BIN=".build/release/afm"
STAGING=".build/release-package-stable"
rm -rf "$STAGING"
mkdir -p "$STAGING"
cp "$BIN" "$STAGING/"
# Provider resource bundles
for BUNDLE_NAME in AFMKit_AFMKitMLX.bundle AFMKit_AFMKitDwarfStar.bundle; do
BUNDLE_DIR="$(dirname "$BIN")/$BUNDLE_NAME"
test -d "$BUNDLE_DIR"
cp -r "$BUNDLE_DIR" "$STAGING/"
done
# WebUI (MANDATORY — verified in Step 5d)
mkdir -p "$STAGING/Resources/webui"
cp "Resources/webui/index.html.gz" "$STAGING/Resources/webui/"
cp README.md "$STAGING/" 2>/dev/null || true
cp LICENSE "$STAGING/" 2>/dev/null || true
STABLE_TARBALL="afm-v${VERSION}-arm64.tar.gz"
tar -czf "$STABLE_TARBALL" -C "$STAGING" .
SHA256=$(shasum -a 256 "$STABLE_TARBALL" | cut -d' ' -f1)
echo "Tarball: $STABLE_TARBALL ($(du -h "$STABLE_TARBALL" | cut -f1 | xargs))"
echo "SHA256: $SHA256"
# MANDATORY: Verify tarball contains webui
if ! tar tzf "$STABLE_TARBALL" | grep -q "index.html.gz"; then
echo "FATAL: Tarball does NOT contain WebUI! Aborting."
ficd "$(git rev-parse --show-toplevel)"
# Return to original branch
git checkout "$ORIGINAL_BRANCH"
git stash pop 2>/dev/null || trueNow apply version changes to the working branch (Steps 8-9 operate on the current branch, not the detached HEAD).
cd "$(git rev-parse --show-toplevel)"
PREV_TAG=$(gh release list --repo scouzi1966/maclocal-api --limit 20 --json tagName -q '.[].tagName' \
| grep '^v[0-9]' | head -1) || true
if [ -n "$PREV_TAG" ] && git cat-file -e "${PREV_TAG}^{commit}" 2>/dev/null; then
CHANGELOG=$(git log --pretty=format:"- %s (\`%h\`)" "${PREV_TAG}..${BUILD_SHA}" -- . ':!vendor' 2>/dev/null)
else
CHANGELOG=$(git log --pretty=format:"- %s (\`%h\`)" -20 -- . ':!vendor' 2>/dev/null)
fi
gh release create "v${VERSION}" \
--title "afm ${VERSION}" \
--notes "$(cat <<EOF
## afm ${VERSION}
Apple Foundation Models + MLX local models — OpenAI-compatible API, WebUI, all Swift.
### Changes since ${PREV_TAG:-first release}
${CHANGELOG}
### Install / Upgrade via Homebrew
**Fresh install:**
\`\`\`
brew tap scouzi1966/afm
brew install scouzi1966/afm/afm
\`\`\`
**Upgrade:**
\`\`\`
brew upgrade afm
\`\`\`
### Install via PyPI
\`\`\`
pip install macafm==${VERSION}
\`\`\`
EOF
)" \
--target "$BUILD_SHA" \
--repo scouzi1966/maclocal-api \
"$STABLE_TARBALL"Note: --target "$BUILD_SHA" ensures the release points to the exact commit used for the build.
IMPORTANT: Do NOT delete, edit, or modify any existing nightly releases. Nightly releases and their nightly-* tags must remain intact on GitHub. This skill creates a NEW stable release — it does not replace any existing release.
DO NOT update the production tap (scouzi1966/afm) until the staging validation completes. The production tap is what every brew install scouzi1966/afm/afm user downloads. Shipping a broken build to it forces a revert — and any user who ran brew update in the broken window already has the bad bottle. Validate on the staging tap first so the real install path is tested against the real published artifact.
The staging tap repo is scouzi1966/homebrew-afm-staging. If it does not exist, create it (public, visible only via explicit tap):
if ! gh repo view scouzi1966/homebrew-afm-staging >/dev/null 2>&1; then
gh repo create scouzi1966/homebrew-afm-staging --public \
--description "Staging Homebrew tap for afm — candidate builds under test, not for production"
fi
STAGING_DIR="${STAGING_TAP_DIR:-$(cd "$(git rev-parse --show-toplevel)/.." && pwd)/homebrew-afm-staging}"
if [ ! -d "$STAGING_DIR/.git" ]; then
gh repo clone scouzi1966/homebrew-afm-staging "$STAGING_DIR"
fiWrite the candidate formula into the staging tap. Use the same URL / version / sha256 as the GitHub release created in Step 8 so the download path matches what the production tap will later serve.
DOWNLOAD_URL="https://github.com/scouzi1966/maclocal-api/releases/download/v${VERSION}/afm-v${VERSION}-arm64.tar.gz"
cd "$STAGING_DIR"
git pull --ff-only 2>/dev/null || true
cat > afm.rb <<RUBY
class Afm < Formula
desc "Apple Foundation Models + MLX local models — OpenAI-compatible API, WebUI, all Swift (STAGING)"
homepage "https://github.com/scouzi1966/maclocal-api"
url "${DOWNLOAD_URL}"
version "${VERSION}"
sha256 "${SHA256}"
depends_on arch: :arm64
depends_on :macos
def install
bin.install "afm"
["AFMKit_AFMKitMLX.bundle", "AFMKit_AFMKitDwarfStar.bundle"].each do |bundle_dir|
(libexec/bundle_dir).mkpath
(libexec/bundle_dir).install Dir["#{bundle_dir}/*"]
bin.install_symlink libexec/bundle_dir
end
if Dir.exist?("Resources/webui")
(share/"afm/webui").mkpath
(share/"afm/webui").install Dir["Resources/webui/*"]
end
end
def caveats
<<~EOS
⚠️ STAGING BUILD — not for production use.
This is v${VERSION} served from a staging tap for validation before
promotion to the main scouzi1966/afm tap. Switch to the main tap
for stable use:
brew untap scouzi1966/afm-staging
brew install scouzi1966/afm/afm
EOS
end
test do
assert_match "v${VERSION}", shell_output("#{bin}/afm --version")
end
end
RUBY
git add afm.rb
git commit -m "Stage candidate v${VERSION}"
git push 2>&1 | tail -3Now provide the user with a machine-portable install-and-verify recipe for a clean host. It MUST be a host that is not the one where the build was produced — the binary has been code-signed ad-hoc and has no special access on the build machine; only a fresh install via the staging tap exercises the same download and TCC path real end users will see.
Use AskUserQuestion:
Question: "Staged v${VERSION} is live at scouzi1966/afm-staging. Please install on a clean machine (ideally not the build host) and run the regression test matrix. Confirm results below."
Install instructions to give the user (absolute brew install form, not the cached tap form):
# On the clean machine — no prior afm install of any kind
brew untap scouzi1966/afm 2>/dev/null # optional: avoid any tap collision during test
brew tap scouzi1966/afm-staging
brew install scouzi1966/afm-staging/afm
afm --version # must show vVERSIONRegression matrix the user should run — at minimum:
afm --version reports v${VERSION}.afm -p 9999 &
sleep 3
curl -s -X POST http://127.0.0.1:9999/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"foundation","stream":false,"messages":[{"role":"user","content":"Say hi"}]}'
kill %1Options:
If the user selects option 2, do NOT proceed to Step 9. The production tap remains on the previous stable. Offer the rollback procedure (below) if the GitHub release needs to be withdrawn.
afm.rb)TAP_DIR="${TAP_DIR:-$(cd "$(git rev-parse --show-toplevel)/.." && pwd)/homebrew-afm}"
cd "$TAP_DIR"
git pull --ff-only
DOWNLOAD_URL="https://github.com/scouzi1966/maclocal-api/releases/download/v${VERSION}/afm-v${VERSION}-arm64.tar.gz"
sed -i '' "s|url \".*\"|url \"${DOWNLOAD_URL}\"|" afm.rb
sed -i '' "s/version \".*\"/version \"${VERSION}\"/" afm.rb
sed -i '' "s/sha256 \".*\"/sha256 \"${SHA256}\"/" afm.rb
sed -i '' "s/assert_match \"v[0-9][^\"]*\"/assert_match \"v${VERSION}\"/" afm.rb
sed -i '' "s/MLX Local Models (v[0-9][^)]*)/MLX Local Models (v${VERSION}+)/" afm.rb
git add afm.rb
git commit -m "afm ${VERSION}"
git pushcd "$(git rev-parse --show-toplevel)"
# BuildInfo.swift
sed -i '' "s/static let version: String? = \".*\"/static let version: String? = \"v${VERSION}\"/" \
Sources/AFMKit/BuildInfo.swift
# pyproject.toml
sed -i '' "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml
# macafm/__init__.py
sed -i '' "s/^__version__ = \".*\"/__version__ = \"${VERSION}\"/" macafm/__init__.py# Install table version
sed -i '' "s/Stable (v[0-9][^)]*)/Stable (v${VERSION})/" README.md
# Release notes link
sed -i '' "s|\[v[0-9][^]]*\](https://github.com/scouzi1966/maclocal-api/releases/tag/v[^)]*)|[v${VERSION}](https://github.com/scouzi1966/maclocal-api/releases/tag/v${VERSION})|" README.md
# "What's new in afm-next" section — reset to show no new features since this stable release.
# All previous nightly features are now IN the stable release, so the list must be cleared.
# Future nightlies will add items back as new features land.
sed -i '' "s/everything in v[0-9][^ ]* plus/everything in v${VERSION} plus/" README.mdIMPORTANT: After updating the version reference, replace the bullet list under "What's new in afm-next" with:
> - No new features yet — nightly is currently in sync with the stable releaseRemove all previous bullet points — they are now part of the stable release and no longer "new in afm-next". Future nightly builds will add new items to this section as features land after the stable cut.
Stage the freshly built binary (from the stable build in Step 5) into the Python package:
cd "$(git rev-parse --show-toplevel)"
BIN=".build/arm64-apple-macosx/release/afm"
[ -x "$BIN" ] || BIN=".build/release/afm"
mkdir -p macafm/bin macafm/share/webui
cp "$BIN" macafm/bin/
# Metallib
METALLIB="$(dirname "$BIN")/AFMKit_AFMKitMLX.bundle/default.metallib"
if [ -f "$METALLIB" ]; then
cp "$METALLIB" macafm/bin/
fi
# WebUI (MANDATORY — verified in Step 5d)
cp "Resources/webui/index.html.gz" macafm/share/webui/
# Build wheel
uv build
# Clean staged assets (never commit binaries)
rm -rf macafm/bin macafm/share
# Verify wheel size (must be >1MB — contains binary)
WHEEL=$(ls -1 dist/macafm-${VERSION}*.whl 2>/dev/null | head -1)
WHEEL_SIZE=$(stat -f%z "$WHEEL" 2>/dev/null || stat -c%s "$WHEEL" 2>/dev/null)
echo "Wheel: $WHEEL (${WHEEL_SIZE} bytes)"
if [ "$WHEEL_SIZE" -lt 1000000 ]; then
echo "ERROR: Wheel is only ${WHEEL_SIZE} bytes — assets were not staged correctly!"
fi
# MANDATORY: Verify wheel contains webui
if ! unzip -l "$WHEEL" | grep -q "index.html.gz"; then
echo "FATAL: Wheel does NOT contain WebUI!"
fiIf wheel is under 1 MB or missing WebUI, STOP. Do not provide the publish command.
Before publishing to PyPI, the user MUST verify both installation methods work from a completely clean slate. Each method must be tested independently with full uninstall in between to avoid conflicts.
First, uninstall ALL existing afm installations:
# Remove all traces
brew uninstall afm 2>/dev/null || true
brew uninstall afm-next 2>/dev/null || true
pip uninstall macafm -y 2>/dev/null || true
uv pip uninstall macafm 2>/dev/null || true
# Verify clean slate
echo "=== Verify clean slate ==="
brew list --formula 2>&1 | grep -E "^afm" || echo "Homebrew: clean"
pip show macafm 2>&1 | grep -i version || echo "pip: clean"
which afm 2>&1 || echo "PATH: clean"Use AskUserQuestion:
Question: "Clean-slate testing required. Please test Homebrew install first, then uninstall before testing pip. Ready to start?"
Provide the user with these commands:
Test 1 — Homebrew (test, then fully uninstall before Test 2):
brew update
brew tap scouzi1966/afm
brew install scouzi1966/afm/afm
afm --version # must show vVERSION
afm mlx -w -m <model> # must launch WebUI in browser
brew uninstall afm # MUST uninstall before Test 2Test 2 — pip from local wheel (after brew is fully removed):
pip install FULL_PATH_TO_WHEEL
afm --version # must show vVERSION
afm mlx -w -m <model> # must launch WebUI in browser
pip uninstall macafm # clean up after testingIMPORTANT: Always provide the full absolute path to the wheel file. The user may run the command from any directory.
Options:
If either test fails, STOP. Investigate the failure, fix the issue, and re-run the failed test before proceeding. Do NOT publish to PyPI until both methods pass.
The maintainer has a TestPyPI account (separate login from production PyPI, 2FA + API token), so the wheel can be validated on TestPyPI before the production push. For a release that feels risky (new packaging, big version jump), offer the TestPyPI dry-run first; otherwise production is fine. The token is supplied at publish time (
--token/UV_PUBLISH_TOKEN/~/.pypirc) — never assume it's stored in the repo.
Present the exact command with the absolute path to the wheel and a placeholder token (production):
ROOT="$(git rev-parse --show-toplevel)"
echo "uv publish --token <YOUR_PYPI_TOKEN> ${ROOT}/dist/macafm-${VERSION}*"TestPyPI dry-run variant (validate first, then verify with pip install --index-url https://test.pypi.org/simple/ macafm==${VERSION}):
echo "uv publish --publish-url https://test.pypi.org/legacy/ --token <YOUR_TEST_PYPI_TOKEN> ${ROOT}/dist/macafm-${VERSION}*"IMPORTANT: Always provide the full absolute path to the dist files — the user may run the command from a different directory.
Use AskUserQuestion:
Question: "The wheel is ready. Validate on TestPyPI first, publish straight to production PyPI, or skip? (Replace the token placeholder with your own.)"
Options:
Homebrew verification:
brew update
brew tap scouzi1966/afm
brew install scouzi1966/afm/afm || brew upgrade afm
# If afm-next is linked, unlink it first: brew unlink afm-next && brew link afm
BREW_VERSION=$(afm --version 2>&1)
echo "Homebrew afm version: $BREW_VERSION"
echo "$BREW_VERSION" | grep -qF "v${VERSION}" && echo "VERSION: PASS" || echo "VERSION: FAIL: expected v${VERSION}"
# Smoke test — run a simple prompt to verify the binary actually works
afm -s "why is the sky blue" 2>&1 | head -20
echo "BREW SMOKE TEST: PASS (if output above contains a response)"PyPI verification (only if user published in Step 13):
VENV_DIR=$(mktemp -d)
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/pip" install macafm==${VERSION}
PIP_VERSION=$("$VENV_DIR/bin/afm" --version 2>&1)
echo "PyPI afm version: $PIP_VERSION"
echo "$PIP_VERSION" | grep -qF "v${VERSION}" && echo "VERSION: PASS" || echo "VERSION: FAIL: expected v${VERSION}"
# Smoke test — run a simple prompt to verify the pip-installed binary works
$VENV_DIR/bin/afm -s "why is the sky blue" 2>&1 | head -20
echo "PIP SMOKE TEST: PASS (if output above contains a response)"
rm -rf "$VENV_DIR"Report PASS/FAIL for each (version check + smoke test). If either fails, investigate and report the issue before proceeding.
cd "$(git rev-parse --show-toplevel)"
git add Sources/AFMKit/BuildInfo.swift pyproject.toml macafm/__init__.py README.md
git commit -m "Release v${VERSION}: promote nightly to stable"Use AskUserQuestion before pushing:
Question: "Version files committed. Push to origin/main?"
Options:
git push origin mainCleanup:
rm -rf ".build/release-package-stable"
rm -f "afm-v${VERSION}-arm64.tar.gz"Final report:
VERSIONBUILD_SHAbrew install scouzi1966/afm/afm / brew upgrade afmpip install macafm==VERSION (if published)diff -q. Likely cause: submodule is at wrong commit or patches were not applied. Re-run git submodule update --init --recursive then ./Scripts/apply-mlx-patches.sh.BuildInfo.swift change was not compiled. Run swift package clean and rebuild.$TAP_DIR is on right branch with no uncommitted changes.uv build fails: Check uv is installed; check pyproject.toml syntax.brew update; check formula with brew audit afm.pip install macafm==VERSION --no-cache-dir.git checkout fails, check for uncommitted changes with git status.If something goes wrong after publishing, use the rollback snapshot from Step 3b to revert. PyPI is immutable — a published version cannot be deleted or replaced. All other channels can be fully reverted.
Use AskUserQuestion before executing rollback:
Question: "Are you sure you want to rollback the v${VERSION} stable release? This will delete the GitHub release, revert the Homebrew tap, and undo version file changes. PyPI cannot be reverted."
Options:
1. Delete GitHub release and tag:
gh release delete "v${VERSION}" --repo scouzi1966/maclocal-api --yes
git tag -d "v${VERSION}" 2>/dev/null || true
git push origin --delete "v${VERSION}" 2>/dev/null || true2. Revert Homebrew tap:
cd "$TAP_DIR"
git revert HEAD --no-edit
git push
# Verify it matches the previous state
grep 'version "' afm.rb
# Should show: PREV_STABLE_VERSION3. Revert version files on working branch:
cd "$(git rev-parse --show-toplevel)"
# Restore previous values
sed -i '' "s/static let version: String? = \".*\"/static let version: String? = \"${PREV_BUILDINFO_VERSION}\"/" \
Sources/AFMKit/BuildInfo.swift
sed -i '' "s/^version = \".*\"/version = \"${PREV_PYPROJECT_VERSION}\"/" pyproject.toml
sed -i '' "s/^__version__ = \".*\"/__version__ = \"${PREV_INIT_VERSION}\"/" macafm/__init__.py
# Restore README
sed -i '' "s/Stable (v[0-9][^)]*)/Stable (v${PREV_PYPROJECT_VERSION})/" README.md
sed -i '' "s|\[v${VERSION}\](https://github.com/scouzi1966/maclocal-api/releases/tag/v${VERSION})|[v${PREV_PYPROJECT_VERSION}](https://github.com/scouzi1966/maclocal-api/releases/tag/v${PREV_PYPROJECT_VERSION})|" README.md
git add Sources/AFMKit/BuildInfo.swift pyproject.toml macafm/__init__.py README.md
git commit -m "Rollback: revert v${VERSION} promotion"Use AskUserQuestion before pushing the rollback commit.
4. PyPI (immutable — cannot rollback):
If the wheel was published to PyPI, it cannot be deleted. Options:
X.Y.Z+1) with the fixpip install twine && twine yank macafm ${VERSION} --repository pypiAfter rollback, report:
v${VERSION}: deleted ✓PREV_STABLE_VERSION ✓© scouzi1966, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/afm-build-promote-nightly of scouzi1966/maclocal-api.
Open the folder on GitHubat commit 138ca5d
Afm Build Promote Nightly next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Afm Build Promote Nightly this skillscouzi1966/maclocal-api | 346 | — | ~8.9k | Automated safety check: Warn | MIT | |
| App Store Deployromankurnovskii/BrewMate | 300 | — | ~813 | Automated safety check: Notes | MIT | |
| Releasevaayne/mori | 303 | — | ~1.2k | Automated safety check: Pass | MIT | |
| Ffmpeg Kit macOS Codesign Crashdivinevideo/divine-mobile | 266 | — | ~1.1k | Automated safety check: Pass | MPL-2.0 | |
| App Store Screenshots GeneratorParthJadhav/app-store-screenshots | 7.2k | — | ~16k | Automated safety check: Pass | MIT | |
| Release Asc CLIrorkai/App-Store-Connect-CLI | 7.7k | — | ~2.4k | Automated safety check: Pass | MIT |
romankurnovskii/BrewMate
Automates the build and deployment of the Electron app to the Mac App Store and TestFlight.
vaayne/mori
Release workflow for Mori macOS workspace terminal and MoriRemote iOS app.
divinevideo/divine-mobile
Fix Flutter macOS app crash at launch due to FFmpeg Kit library loading failure.
ParthJadhav/app-store-screenshots
Scaffolds a Next.js editor for designing App Store and Google Play screenshots as ads and exporting them at every required size, for iOS, Mac and Android.
rorkai/App-Store-Connect-CLI
Publish and verify a new release of the App-Store-Connect-CLI repository.
truongduy2611/app-store-preflight-skills
Scan an iOS/macOS Xcode project for common App Store rejection patterns before submission.
scouzi1966/maclocal-api
Maintain and extend AFM (maclocal-api), a Swift OpenAI-compatible local LLM server and CLI for Apple Foundation Models, MLX models, API gateway proxying, and Vision OCR.
scouzi1966/maclocal-api
Build AFM from scratch — submodules, patches, webui, and Swift build.
scouzi1966/maclocal-api
Run and review the Promptfoo-based AFM agentic evaluation suite.
scouzi1966/maclocal-api
Test a pre-built afm binary at any path — runs pre-flight safety checks, then any combination of unit tests, assertions, smart analysis, promptfoo evals, batch validation, OpenAI compat, GPU…
scouzi1966/maclocal-api
A skill your agent uses when user wants to build a PyPI wheel from an existing compiled afm binary and publish to PyPI.
scouzi1966/maclocal-api
Run the maclocal-api (AFM/MLX) test suite — automated assertions and smart analysis.
Categories
A skill your agent uses when promoting afm to a stable release — builds from main HEAD or a nightly commit, verifies patches, updates Homebrew stable tap (afm.rb), builds a PyPI wheel, updates…. Afm Build Promote Nightly is an agent skill from scouzi1966/maclocal-api.rb), builds a PyPI wheel, updates README and version files, and verifies both brew install and pip install work.
Afm Build Promote Nightly fits situations like: promoting afm to a stable release — builds from main HEAD; A nightly commit; verifies patches; updates Homebrew stable tap (afm.rb).
Run `npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a claude-code`. Or copy the skill folder (.claude/skills/afm-build-promote-nightly in scouzi1966/maclocal-api) into .claude/skills/afm-build-promote-nightly in your project. Claude Code loads it when a task matches its description.
Run `npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a codex`. Or copy the skill folder (.claude/skills/afm-build-promote-nightly in scouzi1966/maclocal-api) into .agents/skills/afm-build-promote-nightly in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add scouzi1966/maclocal-api --skill afm-build-promote-nightly -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/afm-build-promote-nightly, .gemini/skills/afm-build-promote-nightly, .github/skills/afm-build-promote-nightly and .opencode/skills/afm-build-promote-nightly in your project.
Going by SKILL.md and its folder, Afm Build Promote Nightly needs the command-line tools its instructions call (git, brew, gh, pip, swift and uv) and credentials named UV_PUBLISH_TOKEN. Our summary lists: Python 3; Node.js.
SKILL.md names 1 domain. In commands or code: test.pypi.org; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md flagged 1 warning(s): mentions a credentials file (ssh keys, cloud or package-manager tokens). Read the flagged lines before installing; the check is not a guarantee either way.
Afm Build Promote Nightly is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.9k tokens (SKILL.md is roughly 35k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Afm Build Promote Nightly: App Store Deploy (romankurnovskii/BrewMate, 300 stars), Release (vaayne/mori, 303 stars), Ffmpeg Kit macOS Codesign Crash (divinevideo/divine-mobile, 266 stars) and App Store Screenshots Generator (ParthJadhav/app-store-screenshots, 7.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
scouzi1966 (a GitHub user) maintains it in scouzi1966/maclocal-api, which has 346 GitHub stars. The repository holds 12 skills in this directory. The repository was last updated on October 10, 2026.
Source: scouzi1966/maclocal-api on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.