This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Cross-platform desktop orchestrator for parallel Claude Code agents, built with Rust (Tauri 2) and React/TypeScript.
This file is the source of truth for project conventions. Several companion configs read by other AI tools live alongside it and need to stay aligned when the rules here change:
.github/copilot-instructions.md— repo-wide guidance read by GitHub Copilot on every PR review and code suggestion..github/instructions/rust.instructions.md— scoped to**/*.rsviaapplyTofrontmatter..github/instructions/frontend.instructions.md— scoped tosrc/ui/**/*.{ts,tsx,css}..github/instructions/regression-review.instructions.md— scoped to all files; used during PR review for regression-first checking.AGENTS.md— symlink to this CLAUDE.md, read by Codex / OpenCode / other agent tools that look for that filename. Edit CLAUDE.md, never AGENTS.md.
When you change architecture, commands, code-style, regression rules, or god-file lists in this CLAUDE.md, scan the Copilot files for the same statement and update both. Drift here causes Copilot's PR reviews to flag (or miss) issues that don't match what we tell humans.
Always update the user-facing docs in the same PR as the feature change. Adding, removing, or changing user-visible behavior — settings, commands, CLI flags, keyboard shortcuts, environment variables, file locations, plugin manifests, notification triggers — requires a matching docs change. The docs site lives at site/src/content/docs/ (Astro Starlight). Two surfaces matter:
site/src/content/docs/features/<topic>.mdx— the per-feature deep-dive page. New cross-cutting feature → new page. Existing feature gained a knob → update the page. Register new pages in the sidebar nav atsite/astro.config.mjsso they're reachable.site/src/content/docs/features/settings.mdx— the flat settings reference table. Every Settings panel control belongs in this file's matching## <Section>table; add the row when you add the control.
If a change is intentionally undocumented (debug-only flag, internal env var nobody outside the repo should touch), call that out in the PR description so the omission is reviewed, not assumed. CI does not enforce this — the discipline does.
# Backend (Rust)
cargo test -p claudette -p claudette-server -p claudette-cli --all-features # Run all backend tests (CI runs the same crates via cargo llvm-cov)
cargo test -p claudette --test diff_tests # Run a single test file
cargo test -p claudette parse_unified -- --exact # Run a single test by name
cargo clippy -p claudette -p claudette-server -p claudette-cli --all-targets --all-features # Lint (CI command — must pass with zero warnings)
cargo clippy -p claudette-mobile --target aarch64-apple-darwin --all-targets --locked # Mobile lint (CI command — macOS only)
cargo test -p claudette-mobile --locked # Mobile tests (CI command — macOS only)
cargo fmt --all --check # Check formatting
# Frontend (React/TypeScript)
cd src/ui && bun install # Install frontend dependencies
cd src/ui && bun run build # Build frontend (runs tsc -b && vite build)
cd src/ui && bunx tsc -b # TypeScript type check (same as CI)
cd src/ui && bun run test # Run frontend tests (vitest)
cd src/ui && bun run test:watch # Run tests in watch mode
cd src/ui && bun run lint # ESLint check
cd src/ui && bun run lint:css # CSS design-token enforcement (also runs in CI)
# Full app
cargo tauri dev # Dev mode with hot-reload
cargo tauri build # Release buildIMPORTANT: CI sets RUSTFLAGS="-Dwarnings" — all compiler warnings are errors. Fix warnings before committing.
IMPORTANT: Always run cd src/ui && bunx tsc -b after modifying TypeScript files (including tests). CI runs tsc -b via bun run build — vitest does not type-check (it uses esbuild), so tests can pass locally while types are broken. Run tsc -b as the final check before committing any frontend change.
CI also enforces bun install --frozen-lockfile — do not modify bun.lock without intention. CI runs cargo llvm-cov for Rust test coverage (uploaded to Codecov, informational/non-blocking). CI clippy lints claudette, claudette-server, and claudette-cli on Linux, runs scripts/stage-cli-sidecar.sh --profile debug and then cargo test -p claudette-tauri --no-default-features --features devtools,server,voice,alternative-backends,pi-sdk --no-run with the Linux Tauri system libraries installed (--no-run so the binary crate's test targets are compiled too — the test job only covers claudette / -server / -cli, so a non-compiling test fixture in claudette-tauri would otherwise slip through), and checks claudette-mobile on macOS (host target aarch64-apple-darwin, desktop-fallback build — iOS target compilation is intentionally not in CI; it requires Xcode + Apple SDK + cargo tauri ios init scaffolding). The Tauri check uses the shared Rust cache plus Bun's package cache for the Pi harness sidecar staging path; keep it cache-friendly when editing. Frontend CI runs bunx tsc --noEmit as a dedicated type-check step before bun run build.
- Rust edition 2024 — use modern idioms (
let chains,gen blocksif stabilized, etc.) - Default
rustfmtandclippyrules — no custom overrides - Prefer
cargo fmtbefore committing; CI enforces it - TypeScript: strict mode, no
anytypes
- Conventional commits required —
feat:,fix:,docs:,refactor:,test:,ci:,chore:, etc. - Header max 100 characters
- PR titles must also follow conventional commit format (validated by CI)
- Release management is automated via release-please
- GUI: Tauri 2.x (Rust backend) + React/TypeScript (webview frontend)
- State management: Zustand (single store with domain slices)
- Async runtime: Tokio for process management and git operations
- Data persistence: SQLite via rusqlite (bundled)
- Git operations: Shelling out to
gitviatokio::process::Commandfor worktree ops - Agent integration: Claude CLI subprocess with JSON streaming, bridged to frontend via Tauri events. The chat-send resolver dispatches on a per-backend
runtime_harness(AgentBackendConfig::effective_harnessinsrc/agent_backend.rs), not onAgentBackendKinddirectly —kindonly declares which harnesses are valid; the user picks the active one in Settings > Models > Runtime. Ollama / LM Studio default to Pi, OpenAI cards to the Claude CLI gateway, Codex Native to the Codex app-server. Anthropic / Custom Anthropic / Codex Subscription are locked to Claude CLI so subscription OAuth tokens never reach Pi. - Terminal emulation: portable-pty (Rust) + xterm.js (frontend)
- IPC: Tauri commands (
#[tauri::command]) for request/response, Tauri events for streaming
Five crates in a Cargo workspace:
| Crate | Path | Purpose |
|---|---|---|
claudette |
src/ (workspace root) |
Core library — models, db, git, diff, agent logic, WSS transport. No UI or Tauri dependencies. |
claudette-tauri |
src-tauri/ |
Tauri binary (claudette-app). Thin #[tauri::command] wrappers that call into claudette. |
claudette-server |
src-server/ |
WebSocket server for remote access. Also embeddable in the Tauri binary. Uses PersistentSession so interactive controls (AskUserQuestion / ExitPlanMode) work over WSS. |
claudette-cli |
src-cli/ |
Command-line client (claudette binary) that drives the running GUI over a local IPC socket. |
claudette-mobile |
src-mobile/ |
Tauri 2 iOS / Android client — thin WSS remote-control app. Pairs with a running desktop or headless server; doesn't run agents locally. See src-mobile/README.md for the cargo tauri ios init setup. |
Feature flags in claudette-tauri:
default = ["server", "voice", "devtools", "alternative-backends", "pi-sdk"]voice— pulls incpal(audio capture),candle-*(Whisper inference),tokenizers,rubato,hound. Linux requireslibasound2-dev(ALSA); headless builds drop it via--no-default-features --features tauri/custom-protocol,server.devtools— enables Tauri devtools (tauri/devtools)server— optional dep onclaudette-serveralternative-backends— surfaces the user-facing alt-backend gate (Codex Native + the Pi runtime option on Ollama / LM Studio / OpenAI cards). Does not by itself compile the Pi sidecar into the binary; pairs withpi-sdkfor that.pi-sdk— compiles the Pi coding-agent harness (sidecar wrapper around@earendil-works/pi-coding-agent), bundlesbinaries/claudette-pi-harness+binaries/pi/package.json, and exposes the Pi card / Pi runtime option in Settings. Independent ofalternative-backends— drop it (with--no-default-features --features tauri/custom-protocol,server,voice,devtools,alternative-backends -c src-tauri/tauri.no-pi.conf.json) to ship a Codex-Native-enabled build without Pi. The lib crate mirrors this flag (claudette::pi-sdk) and downstreamclaudette-server/claudette-clisetdefault-features = falseon theirclaudettedep so workspace feature unification can't drag Pi back in.
- Vite dev server port is chosen by
scripts/dev.sh(default base 14253, probes for the first free port and exportsVITE_PORT);strictPort: trueso a probe/Vite race fails loudly. Default was moved off Tauri's stock 1420 because other Tauri starter templates default to it and their dev scripts can rebind the port underneath a running webview. - TypeScript enforces
noUnusedLocals,noUnusedParameters,noFallthroughCasesInSwitch - Test runner is vitest (not Jest)
- macOS uses overlay title bar (
titleBarStyle: "Overlay") — affects layout near top of window
Commands in src-tauri/src/commands/ are organized by domain: agent_backends, apps, auth, boot, cesp, chat, claude_flags, cli, community, data, debug, devtools, diagnostics, dialog, diff, env, files, grammars, mcp, metrics, pinned_prompts, plan, plugin, plugins_runtime, remote, repository, scheduling, scm, settings, shell, slash_commands, storage, terminal, updater, usage, voice, workspace. Each is a thin wrapper — business logic belongs in the claudette crate.
claudette (in src-cli/) drives the running GUI over a local IPC socket (Unix domain socket on macOS/Linux, Named Pipes on Windows; interprocess crate). It reuses the same command core as the GUI, so tray, notifications, and workspace list update live. Top-level subcommands include version, capabilities, rpc, workspace (alias ws), chat, repo, batch, plugin, pr, routine, and completion. IPC server lives in src-tauri/src/ipc.rs; CLI surface in src-tauri/src/commands/cli.rs. The CLI requires the GUI to be running — prefer it over poking the SQLite DB directly.
Cargo.toml — workspace root + claudette lib crate
src/
lib.rs — library entry point, re-exports backend modules
db/ — SQLite database (module dir): connection, migrations, CRUD
migrations/ — versioned .sql files + MIGRATIONS registry
git.rs — async git worktree operations
diff.rs — diff parsing and git diff operations
agent/ — agent runtime (module dir): Claude CLI subprocess + JSON streaming,
plus alternative backends (`codex_app_server.rs`, `harness.rs` shared
scaffolding). The Pi modules (`pi_sdk.rs`, `pi_control.rs`) are gated
behind the lib crate's `pi-sdk` feature.
fork.rs — session forking / checkpoint branching
snapshot.rs — workspace snapshots
process.rs — cross-platform process spawning helpers
mcp.rs / mcp_supervisor.rs — MCP server config + lifecycle supervision
plugin.rs — Claude-Code plugin marketplace integration
plugin_runtime/ — sandboxed Lua runtime (mlua) shared across plugin kinds
permissions.rs — tool/permission policy
scm/ — SCM consumer of plugin_runtime: PR/CI types + host/URL detection
env_provider/ — env-provider consumer: dispatcher, mtime cache, merged ResolvedEnv,
plus `nix develop` wrapping for Nix devshell terminals/agents
slash_commands.rs — slash command loading and dispatch
cesp.rs — CESP (Claudette event stream protocol)
config.rs / env.rs / path.rs / file_expand.rs — config, env, path helpers
transport/ — WSS client transport (trait + WebSocket client),
shared by `claudette-tauri` and `claudette-mobile`
model/ — data types (no UI or IO logic); all derive Serialize
names/ — random workspace name generator
ui/ — React/Vite frontend (see src/ui/package.json)
src-tauri/ — Tauri binary crate
src/commands/ — #[tauri::command] wrappers by domain
src/state.rs — managed AppState (db_path, agents, PTYs)
src/pty.rs — PTY management via portable-pty
src/tray.rs — system tray: icon/menu/tooltip, notifications
src/remote.rs — embedded remote server wiring
src/mdns.rs — mDNS advertisement for remote discovery
src/pty_tracker.rs — PTY foreground-process-group polling for the sidebar command indicator (Unix-only; replaced OSC 133)
src-server/ — Standalone + embeddable remote server
plugins/ — bundled Lua plugins (compiled in via include_str!)
scm-github/ — GitHub PR / CI provider
scm-gitlab/ — GitLab PR / CI provider
env-direnv/ — direnv env activation
env-mise/ — mise env activation
env-dotenv/ — `.env` in-process parser
env-nix-devshell/ — Nix devshell detection/env export; terminals and agents enter via
`nix develop` directly instead of through direnv
src-pi-harness/ — TypeScript/Bun sidecar wrapping `@earendil-works/pi-coding-agent`.
Compiled by `scripts/stage-pi-harness-sidecar.sh` into a single Bun
executable at `src-tauri/binaries/claudette-pi-harness-<triple>` and
shipped via Tauri `bundle.externalBin`. Glue lives in
`src/agent/pi_sdk.rs`; only built/loaded when the
`pi-sdk` feature is on.
tests/ — workspace-level Rust integration tests (e.g. `grants_enforcement.rs`
covering the community-plugin granted_capabilities flow).
A single sandboxed Lua runtime (src/plugin_runtime/) serves multiple plugin kinds declared via plugin.json's kind field (scm | env-provider, defaults to scm). Each kind has its own domain consumer (src/scm/, src/env_provider/) that dispatches operations on top of the shared runtime. Bundled plugins live in plugins/*/ and are seeded into the user's plugin dir on first run. Users can drop their own plugins into ~/.claudette/plugins/<name>/ (one plugin.json + one init.lua); discovery picks them up at startup.
Plugin settings (manifest.settings: Vec<PluginSettingField>) let a plugin declare typed user-configurable fields (boolean/text/select) that the Plugins settings section renders as a form. Values persist in app_settings as plugin:{name}:setting:{key} and are piped into the Lua host.config("<key>") surface at invocation time. Manifest defaults apply when no override is set. Global on/off state persists as plugin:{name}:enabled = "false" (absent key = enabled).
Settings UI separates two plugin concepts:
- Plugins (
src/ui/src/components/settings/sections/PluginsSettings.tsx) — Claudette's own Lua plugins (SCM + env-provider). Always visible. Shows status, per-plugin toggle, and the manifest-declared settings form. - Claude Code Plugins (
ClaudeCodePluginsSettings.tsx, route keyclaude-code-plugins) — the Claude CLI marketplace integration fromsrc/plugin.rs(marketplaces, channels, install/uninstall). Always visible in the Settings sidebar nav.
- Data types go in
model/— keep them free of UI and IO dependencies. All model types must deriveSerialize. - Service/IO modules (
db/,git.rs,diff.rs,agent/) live atsrc/level in theclaudettecrate - Tauri commands go in
src-tauri/src/commands/— thin wrappers that call intoclaudette - React components go in
src/ui/src/components/— organized by feature area - State lives in the Zustand store (
useAppStore) — UI state in React, agent sessions in Rust-sideAppState - Streaming data (agent events, PTY output) flows via Tauri events, consumed by React hooks
- Colors and styling use CSS custom properties defined in
styles/theme.css— all colors must bevar(--token-name)references; raw hex/rgba literals anywhere outsidetheme.cssfailbun run lint:css(CI-blocking). Allowed exceptions:rgba(var(--*-rgb), <alpha>)for alpha layering,getPropertyValue(...) || "#..."safety fallbacks intheme.ts, andaccentPreviewswatches in CommandPalette that mirror existing theme hex values.
These files are already large enough that adding more responsibility makes them harder to reason about. When touching them, prefer extracting cohesive behavior into a focused helper / hook / slice / child component near the owning feature, then wire it through the existing entry point. The goal is to reduce or isolate complexity, not pile on another unrelated concern.
- Rust:
src/diff.rs,src/git.rs,src/plugin.rs,src/mcp.rs,src/mcp_supervisor.rs,src-tauri/src/ipc.rs,src-tauri/src/voice.rs, the largest siblings of the recently-splitsrc-tauri/src/commands/agent_backends/directory (gateway_translate.rs,runtime_dispatch.rs,discovery.rs,config.rs), and anything else insrc-tauri/src/commands/*that's already a few hundred lines. - Frontend:
src/ui/src/components/sidebar/Sidebar.tsx,src/ui/src/components/chat/ChatPanel.tsx,src/ui/src/components/chat/ChatInputArea.tsx,src/ui/src/components/terminal/TerminalPanel.tsx,src/ui/src/services/tauri.ts, large CSS modules (e.g.Settings.module.css).
If a god file grew because the right home for the new behavior didn't exist yet, build that home first and land it as a separate (or stacked) commit before adding the new responsibility.
Treat regressions as the main risk before style or aesthetic concerns. Before changing behavior, identify the current contract: persisted DB fields, Tauri command payloads, CLI output, plugin manifests, settings keys, localized strings, terminal/session semantics, worktree behavior, and visible UI workflows. Do not remove, rename, or silently reinterpret any of these unless the task explicitly asks for that change.
If a behavior change is intentional, call it out plainly in the PR summary or review response and add or update tests that pin the new behavior. If it was incidental, preserve compatibility instead. Never fix a type / lint / test failure by deleting tests, removing UI controls, dropping state fields, weakening assertions, or broadening types — fix the underlying mismatch and keep existing user-visible capabilities intact.
- Rust: tests use
tempfile::tempdir()to create ephemeral git repos — no fixtures or test databases. Async tests use#[tokio::test]. Test modules live at the bottom of each file (#[cfg(test)] mod tests). - TypeScript: vitest with
describe/it/expect. Zustand tests reset state viauseAppStore.setState()inbeforeEach. No test database — frontend tests are pure state/logic tests. When constructing fixtures for store state, always read the actual type definition (e.g.,TerminalTabintypes/terminal.ts) — do not guess field names. Look for existingmake*helpers in adjacent test files before creating new fixtures.
- Use
./scripts/dev.sh(not barecargo tauri dev). It probes free Vite + debug-eval ports (bases14253/19432), writes a per-PID discovery file at${TMPDIR:-/tmp}/claudette-dev/<pid>.jsonso/claudette-debugcan find the right instance, stages the CLI sidecar viascripts/stage-cli-sidecar.sh(which also chains toscripts/stage-pi-harness-sidecar.shfor the Pi SDK sidecar), and on macOS adds--runner scripts/macos-dev-app-runner.shso the build is wrapped in a signed.appbundle (usingsrc-tauri/Entitlements.plist) — required for TCC to grant mic/speech permissions to Claudette rather than the terminal. The runner also mirrors the staged sidecars and Pi package metadata into the dev.app'sContents/MacOS/andContents/Resources/binaries/pi/, socurrent_exe.parent()resolution behaves the same way it does in release. Default features used:devtools,server,voice,alternative-backends(override via$CARGO_TAURI_FEATURES;alternative-backendsis always appended). - Bare
cargo tauri devstill works for non-voice changes but does not invoke the macOS runner, probe ports, or write the discovery file. - Non-Nix contributors use
mise(mise.tomlpinsrust = "1.94",bun = "latest",tauri-clifrom npm).make setuprunsmise install+bun install+cargo fetch;make runinvokescargo tauri dev --features devtools,server,alternative-backends. On macOS,mise.tomlforce-setsCC=/usr/bin/ccand per-target Cargo linkers so transitive-liconv/-lSystemlinks succeed undernix-darwin'sld— leave those env entries alone unless you're fixing them.
Do not trigger CoreAudio or Speech.framework permission prompts at app launch — macOS attributes the TCC prompt to that moment, and an unmotivated launch-time prompt looks like spyware.
- Voice setup uses
PlatformSpeechEngine::availability()(prompt-safe; reportsNeedsSpeechPermissionetc.) for status display, andprepare()(triggers the TCC prompt) only insidestart_recording_locked— i.e. when the user actually clicks the mic. There is no startup prewarm; cpal device enumeration is also avoided at launch. - mDNS discovery currently starts at app launch so the sidebar's Nearby list stays populated without extra UI. Changing that affects macOS Local Network prompt timing and should come with an intentional replacement UX.
- Windows builds use MSVC toolchain;
[target.'cfg(windows)'.dependencies]inCargo.tomlpulls in Windows-only crates. Gate Windows-specific code with#[cfg(windows)]/#[cfg(not(windows))]rather than Unix-only paths. - Never spawn a subprocess with a raw
Command::new. A Windows GUI process has no console, so everyCreateProcessWchild without theCREATE_NO_WINDOWflag pops a transient blankcmd.exewindow. Construct commands via theclaudette::processhelper (or call.no_console_window()fromCommandWindowExton everystd::process::Command/tokio::process::Commandbefore.spawn()/.output()/.status()). The only exception is a terminal the user is intentionally opening (cmd/pwsh/wt) — use.new_console_window()there, and only there. This bug is invisible in dev/terminal-launched builds and only surfaces in shipped release builds, so guard at the spawn site. CI enforces this viaclippy.toml'sdisallowed-methods; a rawCommand::newwill fail the build. - Cross-compilation (Nix devshell):
build-win-arm64/build-win-x64viacargo xwin(requiresXWIN_ACCEPT_LICENSE=1);deploy-win-arm64/deploy-win-x64build and push to the test VM. - Ephemeral Windows EC2 (Nix devshell):
aws-win-spinuplaunches a Windows Server 2022 instance (state in.claudette/aws-win/, gitignored);aws-win-rdpopens RDP;aws-win-destroyterminates. Defaults:AWS_PROFILE=dev.urandom.io,AWS_REGION=us-west-2. - CI/nightly/release builds Windows as bare
.exe(no installer), zipped for distribution —cargo xwin build --release --features tauri/custom-protocol.
- Notification sound and commands run on the Rust side (not in the webview) — macOS suspends webview JS when the window is hidden
tray.rshandles attention notifications (AskUserQuestion/ExitPlanMode);commands/chat/handles agent-finished notifications- Both paths use the shared
build_notification_commandhelper incommands/settings.rs—sh -c <cmd>on macOS/Linux,cmd.exe /S /C <cmd>on Windows - Native notification banners:
mac-notification-syson macOS (click-to-navigate),tauri-plugin-notificationon Linux/Windows - Sound playback is platform-specific. macOS/Linux shell out to
afplay/paplay/ffplayinline. Windows usesclaudette::audio(src/audio.rs):PlaySoundWfor built-inC:\Windows\Mediasystem sounds (no decoder, no per-call volume) androdio(cpal + Symphonia, gated to Windows-only deps) for OpenPeon sound packs that ship MP3/OGG/WAV. New Windows audio code belongs inaudio.rsso theunsafeFFI and feature gating stay in one place.
rusqlite::Connectionis notSend— open a fresh connection in each Tauri command viaDatabase::open(&state.db_path)- UI-only state (collapsed sections, panel widths, selection) is NOT persisted — keep in Zustand
- Migrations live as
.sqlfiles undersrc/migrations/and are registered insrc/migrations/mod.rsasMigrationentries in theMIGRATIONSconst slice. - Adding a migration: create
src/migrations/YYYYMMDDHHMMSS_snake_case_description.sqlusing the current UTC timestamp, then append a matchingMigration { id, sql: include_str!(...), legacy_version: None }entry toMIGRATIONS. Migration IDs must be unique — a test enforces this. - Never edit the SQL of a released migration. The
idis the tracked identity; rewriting or renaming applied migrations will desync databases in the field. Fix forward with a new migration. - Merging branches: each new migration is a distinct
.sqlfile and a distinctMIGRATIONSentry, so parallel-branch migrations no longer collide on an integer version — both apply when merged. If two branches happen to choose the same timestamp (e.g. fromdate -u +%Y%m%d%H%M%Srun at the same second), git will surface the clash as a merge conflict inmod.rs; bump one timestamp by a second and rename its file to resolve. PRAGMA user_versionis retained only to seedschema_migrationsonce on pre-redesign databases during the first run of the new runner. Do not read or write it in new code.- "Already exists" leniency: the runner treats
SQLITE_ERRORfailures whose message contains"already exists"or"duplicate column name"as benign — it logs[migrations] <id> skipped: ...to stderr, marks the migration applied, and continues. This makes hand-applied or out-of-order migrations on dev DBs survivable. It does not license writing migrations that depend on this: keep them strictly forward-only and additive, and preferIF NOT EXISTSon newCREATE TABLE/CREATE INDEXstatements.
- PR CI (
.github/workflows/ci.yml): Rust fmt + clippy + tests (coverage → Codecov), desktop Tauri compile check, TypeScript type-check +lint:css+ build + vitest. (ESLint viabun run lintis available locally but not run in CI.) - Nightly (
.github/workflows/nightly.yml): Every push tomain(excluding docs/README). Computes version as<next-minor>-dev.<commit-count>.g<short-sha>, stamps all five Cargo.toml files (workspace root +src-tauri+src-server+src-cli+src-mobile), builds macOS (arm64 + x86_64), Linux (x86_64 + aarch64), Windows (x86_64 + arm64), promotes atomically fromnightly-stagingtonightlytag. - Release (
.github/workflows/release-please.yml): Triggered by Conventional Commits via release-please. Auto-generates CHANGELOG, bumps workspaceCargo.toml(source of truth) and the workspace-member crates (src-tauri,src-server,src-cli,src-mobile), builds all platforms, uploads assets, posts to Discord (DISCORD_WEBHOOK_URLsecret). - Windows builds in CI skip app bundling (bare
.exe, zipped). macOS builds produce.dmg; Linux produces.AppImage+.deb.
- See GitHub Issue #5 for the full MVP PRD
- See GitHub Issue #11 for the Workspace Management TDD
- P0 features: workspace management, agent chat, diff viewer, integrated terminal, checkpoints, git/GitHub integration, scripts, repo settings
- Target platforms: macOS (Apple Silicon + Intel), Linux (x86_64 + aarch64, Wayland + X11), and Windows (x86_64 + ARM64)
A debug TCP eval server runs on 127.0.0.1 in dev builds (default port 19432, overridable via $CLAUDETTE_DEBUG_PORT — scripts/dev.sh probes for a free port so multiple dev instances can coexist). It executes JS in the webview and returns results over TCP. Always use the /claudette-debug skill for debugging — it auto-discovers the right instance via ${TMPDIR:-/tmp}/claudette-dev/<pid>.json and has recipes for state inspection, store tracing, session monitoring, and UAT.
/claudette-debug state # Store overview
/claudette-debug state completedTurns # Dump a specific slice
/claudette-debug eval 'return 1+1' # Arbitrary JS
/claudette-debug monitor start # Background session monitor → /tmp/claudette-debug/monitor.log
/claudette-debug monitor read # Tail monitor log
/claudette-debug snapshot # Full store dumpHelper scripts (use relative paths from project root):
.claude/skills/claudette-debug/scripts/debug-eval.sh— single-shot JS eval.claude/skills/claudette-debug/scripts/debug-monitor.sh— long-running session monitor
Key globals exposed in dev mode:
window.__CLAUDETTE_STORE__— Zustand store (.getState()/.setState())window.__CLAUDETTE_INVOKE__— Tauriinvokefunction
Every successful mic click emits one info-level event on the claudette::voice target with fields provider_id, total_ms (full voice_start_recording command duration), and stream_open_ms (just the cpal input-stream open). Failed starts (permission denied, model missing, recorder open failure, already-active recording) emit an error-level event on the same target via #[tracing::instrument(err)], so cold-start regressions show up regardless of outcome. Watch both in the dev-build console or in ~/.claudette/logs/claudette.<date>.log. Dev builds also emit a Tauri event voice://debug/start_latency (success path only) so the /claudette-debug skill can sample without log tailing. There is no cold/warm split — voice subsystems are intentionally not prewarmed at launch (see the macOS privacy-prompt contract above), so every click is effectively cold. Documented end-to-end under the public Diagnostics page (site/src/content/docs/features/diagnostics.mdx).
All debug code is gated behind #[cfg(debug_assertions)] / import.meta.env.DEV and excluded from release builds. See .claude/skills/claudette-debug/SKILL.md for full docs.
- Add dependencies conservatively — binary size target is < 30 MB
- Cold start target is < 2 seconds to interactive UI
- When choosing crates, prefer well-maintained options with minimal transitive dependencies
- Frontend: use
bunas package manager, not npm