PluginBench
Skill
Pass
Audit score 90

cmux-dev-workflow

manaflow-ai/cmux

Contributor workflow rules for cmux setup, Xcode project normalization, tagged sidebar ExtensionKit development, and dev builds.

What is cmux-dev-workflow?

Establishes standardized practices for setting up the cmux repository, managing Xcode project files, developing tagged sidebar extensions, and building debug variants. Use this when initializing the cmux repo, modifying Xcode project configuration, adding or modifying sidebar ExtensionKit extensions, or working with tagged debug builds to avoid conflicts between concurrent dev builds.

  • Run tagged debug builds with `reload.sh --tag <tag>` to isolate concurrent dev builds and prevent socket/bundle ID conflicts
  • Normalize Xcode project files automatically via pre-commit hook to prevent nondeterministic reordering from reaching commits
  • Manage sidebar extension point identifiers, bundle ID suffixes, and display name suffixes per tag to keep tagged extensions distinct in the OS
  • Build ReleaseFast cmuxd binaries for release and bundling using `zig build -Doptimize=ReleaseFast`
  • Verify tagged sidebar extensions with `pluginkit` and use tag-bound CLI helpers with `CMUX_TAG` environment variable

How to install cmux-dev-workflow

npx skills add null --skill cmux-dev-workflow
Prerequisites
  • Xcode 26.x (pinned in `.xcode-version`)
  • Git with pre-commit hook support
  • zig toolchain for building cmuxd (ReleaseFast optimization)
  • pluginkit command-line tool for verifying sidebar extensions
Claude Code
Cursor
Windsurf
Cline

How to use cmux-dev-workflow

  1. 1.Run `./scripts/setup.sh` once to initialize submodules, build GhosttyKit, and install the pbxproj normalization pre-commit hook
  2. 2.After making code changes, run `./scripts/reload.sh --tag <short-tag>` to build a tagged Debug app (do not use bare `xcodebuild` or untagged builds)
  3. 3.For CLI or socket testing against a tagged Debug app, set `CMUX_TAG=<tag>` and use `scripts/cmux-debug-cli.sh` helper instead of `/tmp/cmux-cli`
  4. 4.When developing sidebar extensions, run `./scripts/reload-extension.sh --tag <tag>` to build a matching tag-scoped sample extension with correct identifiers
  5. 5.Verify tagged sidebar extensions are registered by running `pluginkit -m -p <host-bundle-id>.cmux.sidebar`
  6. 6.For release builds, use `cd cmuxd && zig build -Doptimize=ReleaseFast` instead of debug builds

Use cases

Good for
  • Setting up a fresh cmux repository clone by running `scripts/setup.sh` to initialize submodules and install the pbxproj normalization hook
  • Creating a new tagged debug build after code changes to test in isolation without interfering with other dev builds
  • Developing a new sidebar ExtensionKit sample extension that works correctly with tagged host builds
  • Bumping the Xcode version pin by editing `.xcode-version` and updating the `objectVersion` mapping in CI checks
  • Verifying that a tagged sidebar extension is properly registered with the plugin kit system before testing host integration
Who it's for
  • cmux repository contributors and maintainers
  • Developers building and testing sidebar ExtensionKit extensions
  • Engineers working on concurrent debug builds or multiple feature branches
  • Release engineers bundling cmuxd for distribution

cmux-dev-workflow FAQ

Why must I use `reload.sh --tag <tag>` instead of bare `xcodebuild`?

Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus. Tagged builds get isolated identities so multiple dev builds can coexist without interference.

What does the pbxproj normalization pre-commit hook do?

It runs `scripts/normalize-pbxproj.py` on staged `cmux.xcodeproj/project.pbxproj` to sort high-churn sections, preventing Xcode's nondeterministic reordering from reaching commits. The hook is idempotent and installed by `setup.sh`.

How do I add a new sidebar extension that works with tagged builds?

Add `CMUX_SIDEBAR_EXTENSION_POINT_ID`, `CMUX_BUNDLE_ID_SUFFIX`, and `CMUX_DISPLAY_NAME_SUFFIX` build settings to both app and appex targets, use these variables in Info.plist and bundle identifiers, and ensure the appex is ad-hoc signed by xcodebuild (do not re-sign post-build).

What is the difference between `reload.sh` and `reload-extension.sh`?

`reload.sh --tag <tag>` builds the main cmux Debug app with a tag-scoped extension point. `reload-extension.sh --tag <tag>` builds a matching sample sidebar extension with the correct tag-scoped identifiers and installs it without re-signing.

How do I bump the Xcode version pin?

Edit `.xcode-version` with the new major version, open `cmux.xcodeproj` in the new Xcode (which auto-updates `objectVersion`), and add a case for the new major in `scripts/check-pbxproj.sh` mapping it to the `objectVersion` that major writes.

Full instructions (SKILL.md)

Source of truth, from manaflow-ai/cmux.


name: cmux-dev-workflow description: "Contributor workflow rules for cmux setup, Xcode project normalization, tagged sidebar ExtensionKit development, and dev builds. Use when setting up the cmux repo, changing Xcode project files, adding sidebar extensions, or working with tagged debug builds."

cmux Dev Workflow

Tagged local dev

After making code changes, always run the reload script with a tag to build the Debug app:

./scripts/reload.sh --tag <short-tag>

By default, reload.sh builds but does not launch the app. Pass --launch only when you need to open it automatically.

Never run bare xcodebuild or open an untagged cmux DEV.app. Untagged builds share the default debug socket and bundle ID with other agents, causing conflicts and stealing focus.

For CLI or socket dogfood against a tagged Debug app, use the tag-bound helper and set CMUX_TAG:

CMUX_TAG=<tag> scripts/cmux-debug-cli.sh list-workspaces

Do not use /tmp/cmux-cli for tagged dogfood. That symlink points at the most recently reloaded build.

When rebuilding cmuxd for release/bundling, always use ReleaseFast:

cd cmuxd && zig build -Doptimize=ReleaseFast

Initial setup

Run the setup script to initialize submodules, build GhosttyKit, and install the pbxproj normalization pre-commit hook:

./scripts/setup.sh

Xcode toolchain

The team is pinned to Xcode 26.x. .xcode-version records the major; cmux.xcodeproj/project.pbxproj carries objectVersion = 60, which is what Xcode 26 writes by default. (objectVersion 77 is reserved for projects that adopt synchronized folder groups, which cmux does not use yet. Bumping to a different value requires a deliberate team decision.)

scripts/setup.sh installs a tracked pre-commit hook (scripts/git-hooks/pre-commit) that runs scripts/normalize-pbxproj.py on any staged cmux.xcodeproj/project.pbxproj, sorting the high-churn sections so Xcode's nondeterministic reordering never reaches a commit. The hook is idempotent. CI runs scripts/check-pbxproj.sh to enforce both the objectVersion pin and normalization, so anyone who skips the hook (or never ran setup) gets a clear failure on their PR.

.xcode-version is the single source of truth. To bump the pin: edit .xcode-version, open cmux.xcodeproj in the new Xcode (which rewrites objectVersion automatically when it touches the file), and add a case for the new Xcode major in scripts/check-pbxproj.sh mapping it to the objectVersion that major writes.

Sidebar extension point (dev tagging)

Each tagged dev build gets its own ExtensionKit sidebar extension point so concurrent dev builds don't collide. Three build settings drive this:

  • CMUX_SIDEBAR_EXTENSION_POINT_ID (default com.cmuxterm.app.cmux.sidebar): the extension point identifier baked into Info.plist at build time.
  • CMUX_BUNDLE_ID_SUFFIX (default empty): inserted into the app and appex bundle ids so a tagged extension gets a distinct identity that pkd records separately.
  • CMUX_DISPLAY_NAME_SUFFIX (default empty): appended to the appex CFBundleDisplayName. The OS groups sidebar extensions by display name for the enable/disable + availability counts the host reads (AppExtensionIdentity exposes only bundleIdentifier, localizedName, extensionPointIdentifier, id — cmux already keys its own identity off the stable bundleIdentifier, but the OS-level grouping is by name). Two same-named appexes installed side by side (a base build and a tagged build) are treated as one logical extension, so toggling one perturbs the other; a per-tag display name keeps them distinct.

The host resolves its point id at runtime from the Info.plist key CMUXSidebarExtensionPointIdentifier via CmuxSidebarExtensionPoint.identifier(in:). ./scripts/reload.sh --tag <tag> scopes the host point to com.cmuxterm.app.debug.<tag>.cmux.sidebar. ./scripts/reload-extension.sh --tag <tag> [--host-bundle-id <id>] [--example sample|tabs|both] builds a matching tag-scoped sample extension, passing CMUX_SIDEBAR_EXTENSION_POINT_ID=<host-bundle-id>.cmux.sidebar, CMUX_BUNDLE_ID_SUFFIX=.<tag>, and CMUX_DISPLAY_NAME_SUFFIX=" <tag>". It installs exactly what xcodebuild produced (xcodebuild ad-hoc signs with entitlements intact) — it does NOT re-sign, because a bare codesign --force --sign - strips the appex entitlements and the extension then drops its host XPC connection. pkd ingests the tagged copy because its bundle id is distinct. Verify with pluginkit -m -p <host-bundle-id>.cmux.sidebar.

To author a NEW sample extension that is tag-ready:

  • appex Info.plist: EXAppExtensionAttributes:EXExtensionPointIdentifier = $(CMUX_SIDEBAR_EXTENSION_POINT_ID).
  • add CMUX_SIDEBAR_EXTENSION_POINT_ID (default com.cmuxterm.app.cmux.sidebar), CMUX_BUNDLE_ID_SUFFIX (default empty), and CMUX_DISPLAY_NAME_SUFFIX (default empty) build settings to the app and appex targets in all build configs.
  • PRODUCT_BUNDLE_IDENTIFIER = <appBase>$(CMUX_BUNDLE_ID_SUFFIX) for the app target and <appBase>$(CMUX_BUNDLE_ID_SUFFIX).<leaf> for the appex (suffix before the appex leaf so the appex id stays prefixed by the app id).
  • appex INFOPLIST_KEY_CFBundleDisplayName (or the CFBundleDisplayName Info.plist value) = <Name>$(CMUX_DISPLAY_NAME_SUFFIX).
  • it must be ad-hoc signed by xcodebuild (Info.plist bound, entitlements intact) for pkd to ingest the tagged copy; do not re-sign post-build.

Detailed references

  • Read references/tagged-builds.md for detailed tagged reload, app link, socket, and cleanup behavior.
  • Read references/xcode-project-normalization.md before touching .xcode-version or cmux.xcodeproj/project.pbxproj.
  • Read references/sidebar-extension-tagging.md when changing ExtensionKit sidebar extension identifiers, tagged sample extensions, or pluginkit verification.