diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e8aae01 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,283 @@ +# AGENTS.md - Coding Agent Guide + +This file provides essential information for AI coding agents working in this repository. + +## 📋 Project Overview + +**Tech Stack**: Tauri 2.x Desktop Application +- **Frontend**: Vanilla TypeScript + Vite 6.0.3 +- **Backend**: Rust (edition 2021) +- **Package Manager**: Bun (preferred) or npm +- **Build Tool**: Vite + Tauri CLI +- **Runtime**: mise (for Rust toolchain management) + +**Project Structure**: +``` +tauri-demo/ +├── src/ # Frontend TypeScript/HTML/CSS +│ ├── main.ts # Application entry point +│ ├── styles.css # Global styles +│ └── assets/ # Static resources +├── src-tauri/ # Rust backend +│ ├── src/ # Rust source code +│ │ ├── main.rs # Rust entry +│ │ └── lib.rs # Tauri commands +│ ├── Cargo.toml # Rust dependencies +│ ├── tauri.conf.json # Tauri configuration +│ ├── capabilities/ # Permission configs +│ └── icons/ # Multi-platform icons +├── index.html # HTML entry +├── package.json # Node.js config +├── tsconfig.json # TypeScript config +└── vite.config.ts # Vite config +``` + +## 🔧 Build/Lint/Test Commands + +### Development +```bash +# Start frontend dev server (port 1420) +bun run dev + +# Start Tauri in dev mode (recommended for full app testing) +bun run tauri dev + +# Preview production build +bun run preview +``` + +### Building +```bash +# Build frontend only (TypeScript compile + Vite bundle) +bun run build + +# Build complete Tauri application (all platforms) +bun run tauri build + +# Build for specific platform +bun run tauri build --target x86_64-pc-windows-msvc # Windows +bun run tauri build --target x86_64-apple-darwin # macOS +bun run tauri build --target x86_64-unknown-linux-gnu # Linux +``` + +### Type Checking +```bash +# TypeScript type check (run before build) +tsc --noEmit + +# Watch mode for continuous type checking +tsc --noEmit --watch +``` + +### Rust Commands +```bash +# Check Rust code (fast compile check) +cd src-tauri && cargo check + +# Format Rust code +cd src-tauri && cargo fmt + +# Lint Rust code +cd src-tauri && cargo clippy + +# Run Rust tests +cd src-tauri && cargo test + +# Run a specific Rust test +cd src-tauri && cargo test test_name + +# Run tests with output +cd src-tauri && cargo test -- --nocapture +``` + +### Testing +**Note**: No test framework is currently configured. To add testing: + +**Frontend Testing** (recommended: Vitest): +```bash +bun add -D vitest @vitest/ui +# Run all tests: bun run vitest +# Run single test: bun run vitest path/to/test.test.ts +``` + +**Rust Testing**: Use built-in `cargo test` (see above) + +## 📐 Code Style Guidelines + +### TypeScript/JavaScript + +#### Imports +- Use ES6 imports: `import { foo } from "bar"` +- Tauri API imports: `import { invoke } from "@tauri-apps/api/core"` +- Group imports: external packages first, then local modules +- No unused imports (enforced by `noUnusedLocals`) + +Example: +```typescript +import { invoke } from "@tauri-apps/api/core"; +import { open } from "@tauri-apps/plugin-opener"; + +import { helperFunction } from "./utils"; +``` + +#### Types +- **Always use explicit types** for function parameters and return values +- Use type inference for simple variable assignments +- Prefer `interface` for object shapes, `type` for unions/intersections +- Enable all strict mode checks (already configured in tsconfig.json) + +Example: +```typescript +// Good ✅ +async function greet(name: string): Promise { + return await invoke("greet", { name }); +} + +// Bad ❌ +async function greet(name) { + return await invoke("greet", { name }); +} +``` + +#### Naming Conventions +- **Variables/Functions**: camelCase (`greetUser`, `userName`) +- **Constants**: UPPER_SNAKE_CASE (`MAX_RETRIES`, `API_URL`) +- **Types/Interfaces**: PascalCase (`UserData`, `AppConfig`) +- **Private members**: prefix with underscore (`_internalState`) + +#### Error Handling +- Always handle promises with `async/await` or `.catch()` +- Use try-catch for critical operations +- Provide meaningful error messages + +Example: +```typescript +async function callRustCommand(): Promise { + try { + const result = await invoke("greet", { name: "World" }); + console.log(result); + } catch (error) { + console.error("Failed to call Rust command:", error); + } +} +``` + +### Rust + +#### Formatting +- Use `cargo fmt` (rustfmt) for automatic formatting +- 4 spaces for indentation (standard Rust) +- Line length: 100 characters (rustfmt default) + +#### Naming Conventions +- **Functions/Variables**: snake_case (`greet_user`, `user_name`) +- **Types/Structs/Enums**: PascalCase (`UserData`, `AppState`) +- **Constants**: SCREAMING_SNAKE_CASE (`MAX_CONNECTIONS`) +- **Lifetimes**: lowercase single letter (`'a`, `'static`) + +#### Tauri Commands +- Mark with `#[tauri::command]` attribute +- Use `&str` for string parameters (efficient) +- Return owned types (`String`, not `&str`) +- Register in `invoke_handler!` macro + +Example: +```rust +#[tauri::command] +fn process_data(input: &str, count: i32) -> Result { + if count < 0 { + return Err("Count must be non-negative".to_string()); + } + Ok(format!("Processed {} with count {}", input, count)) +} + +// Register in lib.rs: +.invoke_handler(tauri::generate_handler![process_data]) +``` + +#### Error Handling +- Use `Result` for fallible operations +- Prefer `?` operator for error propagation +- Use `expect()` only when panic is acceptable +- Return descriptive error messages + +Example: +```rust +#[tauri::command] +fn read_config() -> Result { + let data = std::fs::read_to_string("config.json") + .map_err(|e| format!("Failed to read config: {}", e))?; + + serde_json::from_str(&data) + .map_err(|e| format!("Invalid JSON: {}", e)) +} +``` + +## 🔌 Tauri-Specific Patterns + +### Frontend → Rust Communication +```typescript +import { invoke } from "@tauri-apps/api/core"; + +// Simple command +const result = await invoke("command_name", { param: value }); + +// With error handling +try { + const data = await invoke("command", args); +} catch (error) { + console.error("Command failed:", error); +} +``` + +### Adding New Commands +1. Define in `src-tauri/src/lib.rs`: + ```rust + #[tauri::command] + fn new_command(param: &str) -> String { + // implementation + } + ``` + +2. Register in `invoke_handler`: + ```rust + .invoke_handler(tauri::generate_handler![greet, new_command]) + ``` + +3. Call from frontend: + ```typescript + await invoke("new_command", { param: "value" }); + ``` + +## 📦 Dependencies + +### Adding Dependencies + +**Frontend**: +```bash +bun add package-name # Production dependency +bun add -D package-name # Dev dependency +``` + +**Rust**: +```bash +cd src-tauri +cargo add package-name # Production dependency +cargo add --dev package-name # Dev dependency +``` + +## ⚠️ Common Pitfalls + +1. **Port conflicts**: Dev server uses port 1420, HMR uses 1421 +2. **Type mismatches**: Ensure Rust return types match TypeScript expectations +3. **Missing command registration**: New Tauri commands must be added to `generate_handler!` +4. **Permission issues**: Update `src-tauri/capabilities/default.json` for new capabilities +5. **Build errors**: Run `tsc` before `vite build` (automated in build script) + +## 🚀 Workflow Tips + +- Always run `tsc --noEmit` before committing to catch type errors +- Test Tauri commands with `bun run tauri dev`, not just `bun run dev` +- Use `cargo clippy` to catch common Rust mistakes +- Check both frontend and Rust console for errors during development +- Clear Vite cache if seeing stale builds: `rm -rf node_modules/.vite`