Contributing
Prerequisites
- Go 1.25+
- git
Build & Run
# Build all three binaries
go build -o muxd.exe .
go build -o muxd-daemon.exe ./cmd/muxd-daemon
go build -o muxd-hub.exe ./cmd/muxd-hub
# Run
./muxd.exe # TUI client
./muxd-daemon.exe # agent server
./muxd-hub.exe # hub coordinator
# Dev
go vet ./... # lint
Testing
go test ./... # run all tests
go test -v ./... # verbose
go test -race ./... # race detector
go test -cover ./... # coverage
go test -run TestFoo ./... # run specific test
Conventions
- Every exported function and non-trivial unexported function must have tests.
- Use table-driven tests. Name subtests descriptively:
t.Run("returns error on empty input", ...). - Use
testing.Thelpers:t.Helper(),t.Cleanup(),t.TempDir(). - Never use
log.Fataloros.Exitin tests. - Test files live next to the code:
store.go→store_test.go. - Use
testdata/directories for fixtures. - Mock external dependencies with interfaces: never hit real APIs in unit tests.
- For SQLite tests, use an in-memory database:
sql.Open("sqlite", ":memory:"). - Test function naming:
TestTypeName_MethodNameorTestFunctionName.
Code Style
Package structure
This project uses internal/ sub-packages organized by domain. Entry points live in cmd/ directories (cmd/muxd, cmd/muxd-daemon, cmd/muxd-hub). All business logic lives in packages. See Architecture for the full layout.
Naming
- Files: lowercase, single-word or hyphenated. One primary concern per file.
- Types: PascalCase nouns (
Session,Store). - Functions: verb-first for actions (
createSession), noun for getters (sessionTitle). - Variables: camelCase, short but descriptive.
- Acronyms: consistent casing (
apiKey,modelID, notmodelId).
Error handling
- Always check errors. Wrap with
fmt.Errorf("doing thing: %w", err). - Return errors up: don't panic (except for unrecoverable programmer errors).
- Use
errors.Is()for matching, never string comparison.
Functions
- Keep functions under ~50 lines.
- Accept interfaces, return concrete types.
- Bubble Tea
modeluses value receivers (returns modified copy).
Concurrency
- Use Bubble Tea's
Cmdpattern for async work. - When goroutines are needed, ensure they can be cancelled via
context.Contextor channels. - SQLite: single writer, WAL mode, wrap multi-step mutations in transactions.
Formatting
- Code must pass
gofmt. - Run
go vet ./...before committing. - Imports: stdlib first, then third-party, separated by a blank line.
- No unused variables or imports.
What NOT to Do
- Do not add
utils/,helpers/, orcommon/packages. - Do not add interfaces until you have 2+ concrete implementations.
- Do not use
init()functions. - Do not use global mutable state (beyond the
progvariable). - Do not add logging frameworks: use
fmt.Fprintf(os.Stderr, ...). - Do not add CLI frameworks (cobra, urfave/cli).
- Do not use CGo-dependent SQLite drivers.
- Do not commit
.exebinaries or IDE config files.
Pull Requests
- Create a branch from
main. - Make your changes, ensuring tests pass (
go test ./...) and lint is clean (go vet ./...). - Write or update tests for any new or changed behavior.
- Keep commits focused: one logical change per commit.
- Open a PR with a clear description of what changed and why.