Add or change a Tauri command or the mobile HTTP route that shares it. Use when registering a command, a generate_handler entry, a Rust-to-TypeScript wire type, or a wrapper in src/lib/tauri.
Install
npx skillscat add alondero/buildmesh/add-tauri-command Install via the SkillsCat registry.
Add a Tauri command
Follow this in order. The rules it protects are in CLAUDE.md and docs/development/rust-conventions.md. A command validates input, calls one service or db:: function, and maps the error to String.
Do not copy a file listed under docs/development/module-map.md#seam-debt. Those files still open git2 repositories. tests/agent-infra/git2-ownership.test.mjs pins that list.
- Put the command in
src-tauri/src/commands/. Blocking work (SQLite,std::fs,preferences::load,preferences::save, git, network) is a plain sync*_blockingcore plus a#[command]async fnthat callsrun_blockingfromsrc-tauri/src/blocking.rs. A fast in-memory command may stay a sync#[command] fn. - Register it in
tauri::generate_handler!insrc-tauri/src/lib.rs. A missing entry fails at runtime with "command not found". A command that only tests or crash diagnosis need is dev-only: gate the entry with#[cfg(any(debug_assertions, feature = "dev-tools"))]so the release window does not answer the name (issue #2153), and add it to the expected list intests/unit/ipc-contract.test.ts. - If it calls a Tauri core window or app API, grant that capability in
src-tauri/capabilities/default.json. A missing grant fails silently in a production build. - Wire structs derive
TSwith#[ts(export, export_to = "Name.ts")]. Annotatei64,u64, andusizewith#[ts(as = "i32")], andOption<i64>with#[ts(as = "Option<i32>")]. A list of ids on the wire isVec<i32>. Runcargo testwith working directorysrc-tauri/sosrc/types/generatedupdates. Commit the generated files. Never hand-edit them, and never declare a TypeScript interface for the same struct. - Put the desktop wrapper on the facet that already owns the area:
src/lib/tauri/provider.ts,src/lib/tauri/circuitBlueprint.ts,src/lib/tauri/circuitEvidence.ts,src/lib/tauri/history.ts, orsrc/lib/tauri/stateRecovery.ts. Otherwise add it tosrc/lib/tauri.ts. Every wrapper calls_invokefromsrc/lib/tauri/_invoke.ts. Do not callinvokeanywhere else. - A mobile route under
src-tauri/src/http/routesawaits the async command wrapper, or callsrun_blockingitself. It must not call the*_blockingcore on the async worker. - Cover malformed input, a missing dependency, and acknowledged success at that command or route. The guards are
tests/unit/ipc-contract.test.ts,tests/unit/tauri-ipc-seam.test.ts, andtests/unit/async-command-blocking.test.ts. - Document the user-visible behavior or the developer/API contract. A security or lifecycle decision goes in
docs/adr/.
From the worktree root, with NODE_ENV=test:
npx vitest run tests/unit/ipc-contract.test.ts tests/unit/tauri-ipc-seam.test.ts tests/unit/async-command-blocking.test.ts --pool=threads
Rust behaviour: from src-tauri/, cargo test --lib <module>. Build dist/mobile first (npm run build:mobile) when that directory is missing.