Thank you for your interest in contributing to Claudette! This guide will help you get started.
This project adheres to the Contributor Covenant 3.0 Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to seancallan@gmail.com.
Before filing a bug report, please check existing issues to avoid duplicates.
When filing a bug report, include:
- A clear, descriptive title
- Steps to reproduce the issue
- Expected behavior vs. actual behavior
- Your OS and version (macOS, Linux, or Windows)
- Relevant logs or screenshots
Feature suggestions are welcome! Please open an issue describing:
- The problem your feature would solve
- Your proposed solution
- Any alternatives you've considered
- Fork the repository and create a feature branch from
main. - Set up the development environment — see the README for prerequisites.
- Make your changes following the guidelines below.
- Test your changes — run the full test and lint suite before submitting:
cargo test --all-features cargo clippy --workspace --all-targets cargo fmt --all --check cd src/ui && bunx tsc --noEmit
- Commit your changes using conventional commit format.
- Open a pull request against
mainwith a clear description of your changes.
# Clone your fork
git clone https://github.com/<your-username>/claudette.git
cd claudette
# Install frontend dependencies
cd src/ui && bun install && cd ../..
# Run in development mode (macOS / Linux)
./scripts/dev.sh# Windows equivalent — same flags, PowerShell launcher
.\scripts\dev.ps1
.\scripts\dev.ps1 --helpmacOS — voice input dev mode: if you're working on or testing the voice-input feature, run
./scripts/dev.shinstead ofcargo tauri dev. The script wraps the dev binary in a signed.appbundle with the usage strings + entitlements that macOS TCC requires before granting Microphone or Speech Recognition permission. Plaincargo tauri devworks for everything else.
Linux — ALSA development headers: Debian/Ubuntu users need
libasound2-devinstalled (Fedora:alsa-lib-devel, Arch:alsa-lib). Thecpalaudio crate fails to build without it. The full apt install line is in the README prerequisites. Nix users get this automatically viaflake.nix.Headless or sandboxed Linux environments without ALSA can omit voice support entirely:
cargo tauri build --no-default-features --features tauri/custom-protocol,server
Windows — use
scripts\dev.ps1(notcargo tauri dev). The PowerShell launcher refreshes PATH from the registry (so a fresh shell sees clang/cargo without restarting), forces Vite onto127.0.0.1(Windows resolveslocalhostto::1first; WebView2 doesn't follow), dropsvoicefrom the default feature set (gemm-f16requires thefullfp16ARMv8.2 target feature that stock aarch64-pc-windows-msvc doesn't enable), skipstauri/custom-protocol(it would suppressimport.meta.env.DEVand break/claudette-debug), and stages theclaudette-clisidecar that the bundledtauri.conf.jsonwould otherwise look for.--new,--clone,--clean, and--helpwork the same as on the .sh version.Heads up if you have muscle memory for
--clean: the flag was repurposed. It used to mean "run as a fresh user with a per-PID sandbox" — that's now--new. The new--cleanis a standalone action that wipes everything under$TMPDIR/claudette-dev/and exits without launching the app. Use--newfor fresh-user dev sessions and--cleanonly when you want to reset the discovery directory after SIGKILL'd runs.First-time prerequisites on Windows: VS C++ Build Tools (Desktop development with C++ workload) and Clang/LLVM on PATH (e.g.
scoop install llvm). The PowerShell profile snippet for a baredevcommand is in the README — Run in development mode.
Verifying Linux behavior from macOS / Windows: Claudette ships an Arch Linux + XFCE + noVNC test container at
packaging/aur/test/Dockerfile. One command builds the image, installsclaudette-binfrom the in-repo PKGBUILD, plants a Desktop launcher, clones the public repo into~/Projects/ Claudette, and serves the desktop onhttp://localhost:6080/for any browser:scripts/aur/test-in-docker.sh claudette-bin # or: nix develop -c aur-test claudette-binUse it to verify Tauri/GTK behavior, packaging assumptions, or any reported "works on macOS, breaks on Linux" issue without needing to maintain a Linux VM. The user inside the container is
builder/builder(sudo is passwordless). Full reference inpackaging/aur/README.md.
This project uses conventional commits. Every commit message and PR title must follow this format:
<type>: <description>
Common types: feat, fix, docs, refactor, test, ci, chore
- Keep the header under 100 characters
- Use the imperative mood ("add feature" not "added feature")
- PR titles are validated by CI
- Edition 2024 — use modern idioms
- Run
cargo fmtbefore committing (CI enforces formatting) - Run
cargo clippywith zero warnings (CI setsRUSTFLAGS="-Dwarnings")
- Strict mode enabled — no
anytypes - Use
bunas the package manager (not npm)
Claudette is a Tauri 2 desktop app with a Rust backend and React/TypeScript frontend. See CLAUDE.md for the full architecture guide.
Key principles:
- Data types go in
src/model/— keep them free of UI and IO dependencies - Service modules (
db.rs,git.rs,agent.rs) live at thesrc/level - Tauri commands go in
src-tauri/src/commands/as thin wrappers - React components are organized by feature area in
src/ui/src/components/ - State management uses Zustand with domain slices
Claudette is internationalized using i18next on the frontend and a small bespoke loader on the Rust side. Today the app ships with five complete translations: English (en, the baseline), Spanish (es), Brazilian Portuguese (pt-BR), Japanese (ja), and Simplified Chinese (zh-CN). Each ships the full UI plus the system tray menu, notifications, and the quit-confirm dialog. Missing keys fall back to English at runtime, so partial translations are safe to ship — they just leave a few English strings showing through.
Adding a language is mostly a JSON-translation task. You don't need Rust or TypeScript experience to translate the strings; the small registration step at the end is just a handful of lines.
- Frontend —
src/ui/src/locales/<lang>/contains five namespace files:common.json,chat.json,modals.json,settings.json, andsidebar.json. - Backend —
src/locales/<lang>/tray.jsoncontains the tray, notification, and quit-dialog strings.
Both sides use the same {{var}} placeholder syntax for interpolation, so a translator only learns one convention.
- Pick a locale code. Use the 2-letter ISO 639-1 code (e.g.
fr,de,pt). If you need a regional variant, use BCP-47 style (e.g.pt-BR,zh-TW). - Copy the English files. Duplicate
src/ui/src/locales/en/andsrc/locales/en/to your new locale directory. Translate the values; leave the keys byte-for-byte identical to English. - Register the language on the frontend. In
src/ui/src/i18n.ts, add imports for each of the five namespace files, append your locale code toSUPPORTED_LANGUAGES, and add a matching entry to theresourcesmap. - Update the TypeScript declaration in
src/ui/src/types/i18next.d.tsonly if you've introduced a new namespace (most translation contributions don't need this). - Register the language on the backend. In
src/i18n/mod.rs, add a variant to theLocaleenum, add aninclude_str!line for yourtray.json, add a matching*_store()function alongside the existing per-locale stores (each wraps aOnceLockover the parsed JSON), and updateLocale::from_db_valueandLocale::storeto recognize the new code. Also extend thelocales_have_identical_key_setstest in the same file to include your new store, so the parity check covers your locale. - Add the language to the selector in Settings → General, following the existing pattern (
general_language_<code>translation key plus an<option>entry).
If you'd like to fix a mistranslation or polish wording in an existing language, just edit the relevant file under src/ui/src/locales/<lang>/ or src/locales/<lang>/tray.json and open a PR — no registration steps required.
- Keep
{{placeholder}}interpolation tokens verbatim. They're substituted at runtime; translating them will break the rendered string. - Honor
_one/_otherplural keys. If a key has both forms in English (i18next's plural convention), provide both forms in your language even if your language uses fewer plural categories — i18next will pick the right one. - Match casing and punctuation choices to the language's UI conventions, but be aware that some strings (e.g. button labels) have tight space budgets in the UI.
cargo test --all-featuresrunslocales_have_identical_key_setsinsrc/i18n/mod.rs, which fails if the backend locales it includes (currentlyes,pt-BR,ja, andzh-CN, each compared againsten) disagree on which keys exist. The check only covers the locales explicitly compared in the test, so when adding a new backend locale make sure to extend the test to include it (see the registration step above). Run the suite locally before opening a PR.cd src/ui && bunx tsc -benforces frontend type safety, which transitively catches mistyped translation keys consumed viauseTranslation().cargo clippy --workspace --all-targetsandcargo fmt --all --checkround out the standard checks.
If you're not sure where to start, open an issue to claim a language so two contributors don't unknowingly translate in parallel.
- Keep PRs focused — one feature or fix per PR
- Include tests for new functionality where applicable
- Update documentation if your change affects public behavior
- Ensure CI passes before requesting review
- Link related issues in the PR description
By contributing to Claudette, you agree that your contributions will be licensed under the MIT License.