Contributing
Welcome. This page covers code style, the dev loop, tests, and how to add a provider.
Setup
One command:
./scripts/setup.sh # macOS/Linux
./scripts/setup.ps1 # WindowsIt will:
- Check / install Go 1.24+, Rust (stable), Node 18+ — or just run
./scripts/setup.sh(self-healing). - Pull all submodules.
go mod download,cargo fetch.- Build both binaries to
./bin/. - Run a smoke test (start daemon, GET /api/health, kill).
Manual setup is also fine — see getting-started.md.
Dev loop
./scripts/dev.shRuns three things in parallel with hot-reload:
go run ./packages/daemon-go/cmd/relay daemon(watched byair)cargo watch -x runforrelay-ui- A small task-runner that re-runs
relay evalwhenever you edit a profile
Stop with Ctrl+C — kills all three cleanly.
Tests
./scripts/test.shgo test ./...for the Go sidecargo testfor the Rust UIrelay eval --jsonfor routing regressions
CI runs the same in .github/workflows/ci.yml. Tests must
pass before merge.
Code style
Go
gofmtenforced. CI fails on diff.golangci-lint run— clean.- Files end with newline.
- No
panic()in library code; return error. - All interfaces named
FoonotIFoo. - Stream events through channels; don't poll unless explicitly part of the API.
Rust
cargo fmt. CI fails on diff.cargo clippy -- -D warnings— clean.- No
unwrap()in non-test code unless impossible-to-fail (allocation, etc.). - egui draw functions are
fn draw_<thing>(ui: &mut Ui, ...). Keep them short; extract sub-draws. - Use
crate::api::send_*helpers — never callureqfrom inside draw functions.
Commits
- Imperative mood. "Add diff viewer", not "Added".
- Subject ≤ 72 chars. Body wrapped at 72.
- Reference the issue:
Closes #142. - Sign your commits if you have a GPG key
(
git commit -S).
Adding a provider
See providers.md. Five files touched:
internal/adapter/interface.go— addProviderNameconst.internal/adapter/yourtool.go— implementAdapterContract.internal/adapter/registry.go— register.cmd/relay/providers.go— metadata + probe arm.internal/pricing/pricing.go— pricing.
Add a unit test for your Run event parsing using a
recorded fixture.
Adding a UI page
In packages/ui/src/app.rs:
- Add a variant to
NavPage. - Add an icon to
paint_icon(16×16 viewBox, SVG-derived path). - Add a row to
IconRail/FullSidebaritems. - Write
draw_yourpage(ui, state). - Add a
matcharm indraw_central.
If the page needs state, mirror the existing pattern: stash via
ui.ctx().data_mut(|m| m.insert_temp(id, value)). Don't add
fields to RelayApp unless they need to outlive a single
frame.
PR checklist
Areas welcoming contributions
Tagged on GitHub with good first issue:
- Additional secret patterns for the redactor
- More vision-model providers
- Per-provider quota source improvements
- Windows-specific install fixes
- Test recordings for adapter event streams
- Performance tuning of the graph store
Big-ticket roadmap items in docs/architecture.md.
Code of conduct
Be respectful. We follow the Contributor Covenant.
License
Contributions are licensed under Apache-2.0, the same as the project.