We are migrating cmux from a single app target into Swift Packages under
Packages/
. Every new package must satisfy three rules:
Ergonomic.
Public API surface matches what callers naturally want to write. Default to internal access; expose
public
only for types and functions that downstream consumers actually use. Avoid friction such as forcing every call site through a builder or wrapper when a direct API is fine.
No dependency cycles.
Packages form a strict DAG. A package may only depend on packages strictly lower in the graph. When two packages need to share a type, lift it to a common lower-level package or define a protocol seam in the consumer. Every new dependency edge requires re-checking that the graph stays acyclic.
Clear but not overly narrow responsibilities.
A package owns one full domain (e.g.
settings
,
appearance
,
workspace
,
terminal
,
browser
,
command palette
), not a slice of one. A package called "appearance math" or "workspace model" is too narrow — it forces every consumer that touches the surrounding domain to also depend on the sibling slices. Prefer a single
CmuxAppearance
that owns settings, theming, colors, glass, and snapshots together, over
CmuxAppearanceMath
+
CmuxAppearanceTheme
+
CmuxAppearanceSettings
. Don't fragment a domain into
CmuxFooFormatting
+
CmuxFooLogic
+
CmuxFooState
— that's folder structure inside a single package, not module structure. A package boundary exists because more than one consumer needs the contents, or a build/test seam needs to exist.
When in doubt,
extract leaf-first
pull out the package that has no internal dependencies. Consumers in the app target stay put and only update imports. Each leaf shrinks the app target without requiring downstream packages to exist yet.
The existing packages under
Packages/
predate this policy and should not be used as design references.
Wiring a new local package into the project.
cmux.xcodeproj
lists package dependencies explicitly (it is not a synchronized-folder project). Adding
Packages/CmuxFoo
means mirroring an existing package's
project.pbxproj
entries — one
XCLocalSwiftPackageReference
(in the project's
packageReferences
), one
XCSwiftPackageProductDependency
, and a
PBXBuildFile
linked in the Frameworks phase of
every
target that imports it. The app-target packages link into
both
cmux
and
cmux-unit
(so tests can
import
and inject them); copy a recent leaf like
CmuxSocketControl
for the exact shape, then run
scripts/normalize-pbxproj.py
and
scripts/check-pbxproj.sh
. A package the app builds against but
cmux-unit
does not link will compile the app yet fail the test target.
Refactor architecture: layers, Coordinator/Service/Repository, dependency inversion
These higher-level patterns are binding on every new or moved/meaningfully-rewritten file. (The full blueprint, with worked examples and the per-god decomposition, lives in the cmuxterm-hq control repo under
docs/cmux-refactor-audit/blueprint/
; the enforceable core is below.)
Show more
Installs
562
Repository
manaflow-ai/cmux
GitHub Stars
22.8K
First Seen
Jun 8, 2026
Security Audits
Gen Agent Trust Hub
Pass
Socket
Pass
Snyk
Pass