mvsep-cli 0.1.1

CLI tool for MVSEP music separation API
# AGENTS.md - mvsep-rs Development Guide

## Project Overview

`mvsep-rs` is a CLI tool for MVSEP music separation API. It's a single-binary Rust application using `clap` for CLI argument parsing and `reqwest` for HTTP requests.

## Build, Lint, and Test Commands

### Build
```bash
cargo build              # Debug build
cargo build --release    # Release build
```

### Lint & Format
```bash
cargo check             # Type check without full compilation
cargo clippy            # Run lints
cargo fmt               # Format code
cargo fmt --check       # Check formatting without changes
```

### Test
```bash
cargo test              # Run all tests
cargo test <name>       # Run specific test
cargo run               # Run the CLI (debug mode)
```

### Other
```bash
cargo doc               # Generate documentation
cargo tree              # Show dependency tree
cargo update            # Update dependencies
```

## Code Style Guidelines

### General
- This is a single-file project (`src/main.rs`) - keep it that way unless complexity demands modules
- Use Rust edition 2021
- Avoid `unsafe` code unless absolutely necessary

### Imports
```rust
// Group std imports first, then external crates, then local imports
use std::path::PathBuf;
use std::time::Duration;

use clap::{Parser, Subcommand};
use reqwest::multipart;
use serde::{Deserialize, Serialize};
```

### Formatting
- Use 4 spaces for indentation
- Add blank lines between logical sections
- Use trailing commas in match arms and struct literals
- Maximum line length: 100 characters (soft limit)

### Naming Conventions
- **Structs/Enums**: PascalCase (`CliConfig`, `TaskStatus`)
- **Functions**: snake_case (`get_proxy`, `build_client`)
- **Variables**: snake_case (`proxy_host`, `output_dir`)
- **Constants**: SCREAMING_SNAKE_CASE (`CONFIG_FILE`, `VERSION`)
- **Fields in structs**: snake_case
- **Option/Result types**: Use clear names like `proxy_host: Option<String>`

### Error Handling
- Use `Result<T, Box<dyn std::error::Error>>` for async functions
- Use `?` operator for error propagation
- Provide meaningful error messages with context
- Use `unwrap_or_else` for fallible operations with custom error handling

```rust
// Good examples
let config = CliConfig::load();
std::fs::write(config_path, content)?;
config.save()?;

// With custom fallback
let host = std::env::var("PROXY_HOST")
    .ok()
    .or_else(|| config.proxy_host.clone())
    .unwrap_or_else(|| "127.0.0.1".to_string());
```

### Types
- Use explicit types for function parameters and return values
- Use `i32` for CLI arguments and API IDs
- Use `String` for owned strings, `&str` for string slices
- Use `Option<T>` for optional values

### Struct Derives
- Derive `Debug`, `Deserialize`, `Serialize` for data structures
- Derive `Clone` when needed
- Derive `Default` for default-constructible types

```rust
#[derive(Debug, Deserialize, Serialize, Default)]
struct CliConfig {
    api_token: Option<String>,
    output_dir: Option<String>,
}

#[derive(Debug, Deserialize, Clone)]
struct TaskStatus {
    status: String,
    data: Option<TaskStatusData>,
}
```

### Async/Await
- Use `async fn` for asynchronous operations
- Use `tokio` with `full` features for runtime
- Prefer `.await` over blocking calls

### CLI Design (clap)
- Use `#[derive(Parser)]` and `#[derive(Subcommand)]`
- Add doc comments for CLI help text
- Use `#[arg(...)]` for argument attributes
- Group related options with clear names

```rust
#[derive(Parser)]
#[command(name = "mvsep-cli")]
#[command(about = "MVSEP CLI - Music separation tool")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Test connection to API
    Test,
    
    /// Create a separation task
    Create {
        /// Audio file path
        file: String,
        
        #[arg(short = 't', long = "sep-type")]
        sep_type: i32,
    },
}
```

### HTTP Client
- Use `reqwest` with `multipart` and `json` features
- Create reusable client with `build_client()` function
- Use `Client::builder()` for custom configuration

### Configuration
- Store config in `~/.mvsep_cli_config` (HOME directory)
- Use JSON for config serialization
- Load config lazily with `CliConfig::load()`
- Support environment variable overrides where appropriate

## Common Patterns

### Config Pattern
```rust
#[derive(Debug, Deserialize, Serialize, Default)]
struct CliConfig {
    api_token: Option<String>,
    // ... other fields
}

impl CliConfig {
    fn load() -> Self {
        let config_path = Self::config_path();
        if config_path.exists() {
            if let Ok(content) = std::fs::read_to_string(&config_path) {
                if let Ok(config) = serde_json::from_str(&content) {
                    return config;
                }
            }
        }
        Self::default()
    }
    
    fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
        let content = serde_json::to_string_pretty(self)?;
        std::fs::write(Self::config_path(), content)?;
        Ok(())
    }
}
```

### Async Handler Pattern
```rust
async fn create_task(file: &str) -> Result<String, Box<dyn std::error::Error>> {
    let client = build_client()?;
    // ... implementation
    Ok(result)
}
```

## Adding New Commands

1. Add variant to `Commands` enum with doc comment
2. Add variant handler in `main()` match statement
3. Use helper functions for complex logic
4. Test the command manually before committing

## Dependencies

Key dependencies (see `Cargo.toml`):
- `clap` (v4) - CLI parsing with derive
- `reqwest` (v0.12) - HTTP client with multipart/json
- `tokio` (v1) - Async runtime
- `serde` + `serde_json` - Serialization