Contributing
Prerequisites
- Go 1.25+
- git
Build & Run
go build -o muxd.exe . # build
go run . # run (new session)
go run . -c # resume latest session
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 (domain, config, store, provider, tools, agent, checkpoint, daemon, service, tui, telegram). main.go is pure wiring. 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.