Worked example: Acme Studio
This walkthrough ships Acme Studio from a fork at acme-corp/frogg. It uses
brands/example, which is already in the repository, so every command here works on a
fresh clone. Output shown is real output from this repository at 0.6.19.
The brand
Section titled “The brand”{ "schemaVersion": 1, "id": "acme", "name": "Acme Studio", "applicationId": "com.acme.studio", "daemonPort": 10099, "assets": { "icon": "./icon.svg" }, "colors": { "light": { "accent": "#6d28d9", "accentForeground": "#ffffff", "background": "#faf5ff", "foreground": "#27143d" }, "dark": { "accent": "#c4b5fd", "accentForeground": "#27143d", "background": "#20152e", "foreground": "#faf5ff" } }}icon.svg is a 1024 × 1024 purple tile with a white “A”. There is no distribution
block, so updates are disabled, and no services, so there is no hosted pairing page or
relay.
1. Fork, clone and check
Section titled “1. Fork, clone and check”-
Fork
frogg-app/froggtoacme-corpon GitHub and enable Actions in the fork. -
Clone and add upstream:
Terminal window git clone https://github.com/acme-corp/frogg.git acme-studio && cd acme-studiogit remote add upstream https://github.com/frogg-app/frogg.gitnpm ci -
Copy the example into a directory you own, so later upstream changes to the example never touch your product:
Terminal window cp -r brands/example brands/acme -
Check it:
Terminal window npm run --silent brand:check -- --brand brands/acmeAcme Studio: valid (com.acme.studio); updates disabled
2. Point updates at the fork
Section titled “2. Point updates at the fork”Add distribution details so releases, updates and installers use acme-corp/frogg:
"publisher": "Acme Corp", "links": { "website": "https://studio.acme.example", "support": "https://github.com/acme-corp/frogg/issues" }, "distribution": { "repository": "acme-corp/frogg", "updates": "github-release" }npm run --silent brand:check -- --brand brands/acmeAcme Studio: valid (com.acme.studio); updates github-releaseCommit the brand on main:
git add brands/acmegit commit -m "Add Acme Studio brand"git push origin main3. Run it locally
Section titled “3. Run it locally”export FROGG_BRAND_DIR=brands/acmenpm run dev:server # terminal 1: listens on 0.0.0.0:10099, state in .dev/acme-homenpm run dev:app # terminal 2: Expo on 10100Open http://<your-host>:10100 in a browser, or run npm run dev:desktop for the
Electron shell. The window, icon, favicon, accent colour and product name are Acme’s.
To try the production shape instead (daemon serving its own web UI):
export FROGG_BRAND_DIR=brands/acmenpm run build:server && npm run build:daemon-web-uiACME_HOME=/tmp/acme-home node apps/cli/bin/frogg start --listen 0.0.0.0:10099 --web-uiThe CLI built from this checkout is branded, even though the in-repo launcher file is
still called frogg:
$ node apps/cli/bin/frogg --helpUsage: acme [options] [command]
Acme Studio CLI - control your AI coding agents from the command line$ node apps/cli/bin/frogg statusKEY VALUELocal Daemon runningAcme Studio Home /tmp/acme-homeListen 0.0.0.0:10099Relay disabled...4. Coexist with stock frogg
Section titled “4. Coexist with stock frogg”Acme and frogg can run on the same machine. Their identities do not overlap:
| Resource | frogg | Acme Studio |
|---|---|---|
| CLI | frogg |
acme |
| State | ~/.frogg |
~/.acme |
| Env override | FROGG_HOME |
ACME_HOME |
| Port | 9999 | 10099 |
| systemd unit | frogg-daemon |
acme-daemon |
| launchd label | app.frogg.frogg-daemon |
com.acme.studio-daemon |
| URL scheme | frogg:// |
acme:// |
| App ID | app.frogg.frogg |
com.acme.studio |
| Desktop binary | frogg |
acme (Electron executable uses the brand id) |
| Skills installed for agents | frogg, frogg-dev, … |
acme-frogg, acme-frogg-dev, … |
Branding CI runs both daemons side by side to check this
(scripts/ci/branding-runtime-isolation.mjs).
5. Configure CI
Section titled “5. Configure CI”gh variable set FROGG_BRAND_DIR --body brands/acme --repo acme-corp/frogg
# Android release signing (do this before the first release)keytool -genkeypair -v -keystore acme-release.keystore -alias acme -keyalg RSA -keysize 4096 -validity 10000gh secret set FROGG_ANDROID_KEYSTORE_BASE64 --body "$(base64 -w0 acme-release.keystore)" --repo acme-corp/frogggh secret set FROGG_ANDROID_KEYSTORE_PASSWORD --repo acme-corp/frogggh secret set FROGG_ANDROID_KEY_ALIAS --body acme --repo acme-corp/frogg
# Optional: Windows signing, Docker Hubgh secret set AZURE_TENANT_ID --repo acme-corp/frogg# ... AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, TRUSTED_SIGNING_ENDPOINT, TRUSTED_SIGNING_ACCOUNT, TRUSTED_SIGNING_PROFILETry a test build before tagging anything:
gh workflow run build-selected.yml --repo acme-corp/frogg -f target=allgh run list --repo acme-corp/frogg --workflow build-selected.yml --limit 1The run uploads windows-x64-client-<sha>, linux-x64-daemon-<sha> and
android-arm64-debug-signed-<sha> artifacts named with the acme prefix.
6. First release (beta)
Section titled “6. First release (beta)”npm run version:all:beta:patch # 0.6.19 -> 0.6.20-beta.1, commits the bumpAdd a changelog section, amend it into the release commit, then tag and push:
## 0.6.20-beta.1 — 2026-09-20
- First Acme Studio beta, based on frogg 0.6.19.git add CHANGELOG.md && git commit --amend --no-editgit tag -d v0.6.20-beta.1 2>/dev/null # npm version tagged the pre-amend commitnpm run release:push # tags HEAD as v0.6.20-beta.1 and pushesThe Release workflow creates a draft Acme Studio 0.6.20-beta.1 prerelease, builds
every platform, uploads acme-... assets and the generated installers, then publishes.
Install the daemon from it on a test host, and the desktop app from the release page:
curl -fsSL https://github.com/acme-corp/frogg/releases/download/v0.6.20-beta.1/install.sh | bashacme statusShip a second beta the same way and confirm the desktop app (on the beta channel) and
acme update --channel beta both move to it. Then release stable with
npm run version:all:promote and npm run release:push.
7. Keep going
Section titled “7. Keep going”- Merge upstream regularly.
- Contribute shared fixes back from
branches based on
upstream/main.