.claude/skills/tauri-2/tauri-agents/tauri-agents-project-scaffolder/SKILL.md
Use when scaffolding a new Tauri 2 project, setting up initial project structure, or generating boilerplate code. Prevents incomplete scaffolding with missing permission files, unregistered commands, or broken IPC bridges. Covers configured plugins, capability files, Rust commands with TypeScript invoke calls, build pipeline, and frontend integration. Keywords: tauri scaffolder, project generator, boilerplate, scaffold, new project, project structure, code generation.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer tauri-agents-project-scaffolderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Execute these steps in order when creating a new Tauri 2 project:
1. Gather requirements (app type, plugins, frontend framework)
2. Generate project structure
3. Configure Cargo.toml with dependencies
4. Configure tauri.conf.json
5. Create lib.rs with commands and plugin registration
6. Create main.rs (desktop entry point)
7. Create capability files
8. Create custom command permissions
9. Create frontend invoke wrappers
10. Create .gitignore
11. Verify completeness
ALWAYS determine these before generating files:
[ ] App type: basic | multi-window | tray-app | mobile-ready
[ ] Frontend framework: vanilla | React | Vue | Svelte | SolidJS
[ ] Bundler: Vite (default) | Webpack | other
[ ] Plugins needed: fs | dialog | store | shell | http | notification | others
[ ] Target platforms: desktop-only | desktop+mobile | all
[ ] Feature requirements: auto-updater | tray icon | custom protocol | menus
my-tauri-app/
├── src/ # Frontend
│ ├── index.html
│ ├── main.ts
│ ├── styles.css
│ └── lib/
│ └── tauri.ts # Invoke wrappers
├── src-tauri/
│ ├── src/
│ │ ├── lib.rs # App logic + command registration
│ │ ├── main.rs # Desktop entry point
│ │ ├── commands.rs # Command implementations
│ │ └── error.rs # Error types
│ ├── capabilities/
│ │ └── default.json # Permission grants
│ ├── permissions/
│ │ └── commands.toml # Custom command permissions
│ ├── icons/ # App icons (all sizes)
│ ├── Cargo.toml
│ ├── Cargo.lock
│ ├── build.rs
│ └── tauri.conf.json
├── package.json
├── tsconfig.json
├── vite.config.ts
└── .gitignore
Adds to basic structure:
src-tauri/
├── capabilities/
│ ├── main-window.json # Main window permissions
│ └── settings-window.json # Settings window permissions
Adds to basic structure:
src-tauri/src/
├── tray.rs # Tray icon setup + menu
[package]
name = "app"
version = "0.1.0"
description = "A Tauri 2 application"
edition = "2021"
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
# Plugins -- uncomment as needed:
# tauri-plugin-opener = "2"
# tauri-plugin-fs = "2"
# tauri-plugin-dialog = "2"
# tauri-plugin-store = "2"
# tauri-plugin-shell = "2"
# tauri-plugin-http = "2"
# tauri-plugin-notification = "2"
# tauri-plugin-clipboard-manager = "2"
# tauri-plugin-os = "2"
# tauri-plugin-process = "2"
# tauri-plugin-updater = "2"
# tauri-plugin-global-shortcut = "2"
# tauri-plugin-window-state = "2"
Rules:
serde, serde_json, thiserrortokio with full features for async commandscrate-type with all three types for mobile compatibilitytauri-build in build-dependencies{
"$schema": "./gen/schemas/desktop-schema.json",
"productName": "My App",
"version": "0.1.0",
"identifier": "com.yourcompany.myapp",
"build": {
"devUrl": "http://localhost:5173",
"frontendDist": "../dist",
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build"
},
"app": {
"windows": [
{
"label": "main",
"title": "My App",
"url": "/",
"width": 800,
"height": 600,
"minWidth": 400,
"minHeight": 300,
"resizable": true,
"center": true
}
],
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; connect-src ipc: http://ipc.localhost",
"freezePrototype": true
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/[email protected]",
"icons/icon.icns",
"icons/icon.ico"
]
},
"plugins": {}
}
Rules:
identifiercsp (never null)freezePrototype: truebeforeBuildCommand and beforeDevCommandmod commands;
mod error;
use tauri::Manager;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
// -- Plugins --
.plugin(tauri_plugin_opener::init())
// .plugin(tauri_plugin_fs::init())
// .plugin(tauri_plugin_dialog::init())
// .plugin(tauri_plugin_store::init())
// -- State --
// .manage(std::sync::Mutex::new(AppState::default()))
// -- Commands --
.invoke_handler(tauri::generate_handler![
commands::greet,
])
.setup(|app| {
// Initialization that depends on App
let _handle = app.handle().clone();
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Rules:
#[cfg_attr(mobile, tauri::mobile_entry_point)]generate_handler![]pub in this file// Prevents additional console window on Windows in release
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
app_lib::run();
}
use serde::Serialize;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("{0}")]
Custom(String),
}
impl Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::ser::Serializer {
serializer.serialize_str(self.to_string().as_ref())
}
}
pub type AppResult<T> = Result<T, AppError>;
use crate::error::AppResult;
#[tauri::command]
pub fn greet(name: String) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
fn main() {
tauri_build::build();
}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default capabilities for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"core:window:default",
"core:app:default",
"allow-greet"
]
}
Rules:
core:default[[permission]]
identifier = "allow-greet"
description = "Allow the greet command"
commands.allow = ["greet"]
Rules:
allow-<command-name>import { invoke } from '@tauri-apps/api/core';
export async function greet(name: string): Promise<string> {
return invoke<string>('greet', { name });
}
Rules:
# Dependencies
node_modules/
# Build output
dist/
src-tauri/target/
src-tauri/gen/
# Environment
.env
.env.*
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Do NOT ignore Cargo.lock -- it ensures deterministic builds
app.windows[] with "visible": false, "create": trueshow_settings command using app.get_webview_window("label")trayIcon config in app section of tauri.conf.jsonsetup() using TrayIconBuilder and MenuBuilderCloseRequested event to hide instead of close: api.prevent_close()After scaffolding, verify:
[ ] Cargo.toml: all needed plugins listed
[ ] lib.rs: all plugins initialized with .plugin()
[ ] lib.rs: all commands in generate_handler![]
[ ] lib.rs: state registered with manage() if needed
[ ] capabilities/: permissions for all plugins and commands
[ ] permissions/: TOML file for each custom command
[ ] Frontend: invoke wrapper for each Rust command
[ ] Frontend: @tauri-apps/api and plugin packages in package.json
[ ] tauri.conf.json: identifier is unique
[ ] tauri.conf.json: CSP is set
[ ] tauri.conf.json: build commands configured
[ ] .gitignore: target/ excluded, Cargo.lock NOT excluded
[ ] build.rs: calls tauri_build::build()
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.