# Tools Shared Utilities Integration Guide This guide explains how to integrate shared utilities into your Tools ecosystem Rust CLI. ## Quick Start ### 1. Add Dependency to Cargo.toml In your CLI crate (e.g., `crates/your-cli/Cargo.toml`): ```toml [dependencies] tools-shared = { path = "../../../shared/rust" } # For interactive project selection tools-shared = { path = "../../../shared/rust", features = ["interactive"] } # For manifest management tools-shared = { path = "../../../shared/rust", features = ["manifest"] } # All features tools-shared = { path = "../../../shared/rust", features = ["interactive", "manifest"] } ``` ### 2. Import in main.rs ```rust use tools_shared::{ // Configuration discovery find_config_path, find_db_path, // Project features detect_project_from_cwd, SelectableProject, select_project, // Database migrations find_migration_files, run_migration_file, // XDG directories tool_data_dir, tool_config_dir, ensure_dir, // Global database find_global_db_path, }; ``` ### 3. Use in Your Code See examples below for each feature. ## Feature Integration Examples ### Configuration Discovery Find configuration files automatically: ```rust use tools_shared::find_config_path; #[derive(Parser)] struct Cli { #[arg(short, long)] config: Option, } #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); // Use provided config or search let config_path = cli.config .map(PathBuf::from) .or_else(|| find_config_path("my-tool.toml")) .ok_or_else(|| anyhow::anyhow!("No config found"))?; let config = Config::load(&config_path)?; Ok(()) } ``` ### Project Auto-Detection Add a global `--project` flag that auto-detects projects: ```rust use tools_shared::detect_project_from_cwd; #[derive(Parser)] struct Cli { #[arg(short, long, global = true, help = "Project name (auto-detected if omitted)")] project: Option, #[command(subcommand)] command: Commands, } #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); // Auto-detect if not specified let project_name = cli.project .clone() .or_else(|| { detect_project_from_cwd("config.toml") .map(|p| p.name) }); if let Some(name) = project_name { println!("Working on project: {}", name); } else { println!("No project specified or detected"); } Ok(()) } ``` ### Interactive Project Selection Add a command that lets users select a project: ```rust use tools_shared::{select_project, SelectableProject}; #[derive(Subcommand)] enum Commands { /// Select a project interactively SelectProject, } async fn select_project_command(projects: Vec) -> Result { let selectable: Vec = projects .into_iter() .map(|p| SelectableProject { id: p.id.clone(), name: p.name, path: None, metadata: Some(format!("Phase: {:?}", p.current_phase)), }) .collect(); select_project(selectable, "Select a project:", None) .map_err(|e| anyhow::anyhow!("Selection failed: {}", e)) } ``` ### Database Migrations Add migration support: ```rust use tools_shared::{find_migration_files, run_migration_file}; #[derive(Subcommand)] enum Commands { /// Run migrations Migrate { /// Path to migration file #[arg(long)] file: Option, /// Find and run all migrations in data/migration/ #[arg(long)] all: Option, }, } async fn handle_migrate(file: Option, all: Option) -> Result<()> { if let Some(sql_file) = file { // Run single migration run_migration_file(&sql_file)?; println!("✅ Migration executed"); } else if let Some(root) = all { // Find and run all migrations let migrations = find_migration_files(&root)?; println!("Found {} migrations", migrations.len()); for migration in migrations { if let Some(name) = migration.file_name() { println!("Running: {:?}", name); // Execute migration (requires sqlx or other DB driver) } } } Ok(()) } ``` ### XDG Directories for Data Storage Use standard directories for your tool's data: ```rust use tools_shared::{tool_data_dir, tool_config_dir, ensure_dir}; use std::path::PathBuf; #[tokio::main] async fn main() -> Result<()> { let tool_name = "my-tool"; // Create standard directories let data_dir = tool_data_dir(tool_name); let config_dir = tool_config_dir(tool_name); ensure_dir(&data_dir)?; ensure_dir(&config_dir)?; // Now use them let db_path = data_dir.join("my-tool.db"); let config_path = config_dir.join("config.toml"); println!("Data: {}", db_path.display()); println!("Config: {}", config_path.display()); Ok(()) } ``` ### Global Multi-Project Database Use a single global database to manage multiple projects: ```rust use tools_shared::find_global_db_path; use sqlx::sqlite::SqlitePool; #[tokio::main] async fn main() -> Result<()> { // Get global database path let db_path = find_global_db_path("my-tool"); let db_url = format!("sqlite://{}", db_path.display()); // Create database if it doesn't exist if !db_path.exists() { std::fs::create_dir_all(db_path.parent().unwrap())?; } // Connect to database let pool = SqlitePool::connect(&db_url).await?; // Run migrations sqlx::query("CREATE TABLE IF NOT EXISTS projects ( id TEXT PRIMARY KEY, name TEXT NOT NULL, phase TEXT NOT NULL )") .execute(&pool) .await?; Ok(()) } ``` ## Integration Checklist When adding shared features to your CLI: ### Configuration Discovery - [ ] Add `tools-shared` dependency - [ ] Update `Cargo.toml` help text to document search order - [ ] Use `find_config_path()` to discover config files - [ ] Test configuration discovery from multiple locations ### Project Detection - [ ] Add global `--project` flag to CLI struct - [ ] Implement fallback to `detect_project_from_cwd()` - [ ] Add `project detect` command to show detected project - [ ] Cache project selection with `save_last_project()` ### Project Selection - [ ] Add `features = ["interactive"]` to tools-shared - [ ] Create struct list of available projects - [ ] Implement project selection command - [ ] Use `select_project()` for interactive picker ### Database Migrations - [ ] Add migration file discovery - [ ] Create `data/migration/` directory in examples - [ ] Implement migration commands (`migrate --file` and `migrate --all`) - [ ] Create sample migration files for testing ### XDG Directories - [ ] Use `tool_data_dir()` for user data - [ ] Use `tool_config_dir()` for configuration - [ ] Use `tool_cache_dir()` for temporary data - [ ] Call `ensure_dir()` before writing files ### Global Database - [ ] Use `find_global_db_path()` for database location - [ ] Create database parent directories if missing - [ ] Store per-project data with project IDs - [ ] Document multi-project schema ### Testing - [ ] Test all configuration discovery paths - [ ] Test project detection in various directories - [ ] Test migration file discovery and execution - [ ] Test directory creation with `ensure_dir()` - [ ] Test database connectivity ### Documentation - [ ] Update help text with configuration search order - [ ] Document `--project` flag usage - [ ] Add examples to README.md - [ ] Document migration file format ## File Structure for a New Tool When creating a new tool with shared features: ``` my-tool/ ├── Cargo.toml # Workspace root ├── README.md # Tool documentation ├── QUICKSTART.md # 5-minute setup guide ├── crates/ │ ├── my-core/ # Core library │ │ ├── src/ │ │ │ ├── lib.rs │ │ │ ├── error.rs │ │ │ └── service.rs │ │ └── Cargo.toml │ │ │ └── my-cli/ # CLI using shared features │ ├── src/ │ │ ├── main.rs # Imports tools-shared │ │ └── handlers/ # Command handlers │ └── Cargo.toml # Has tools-shared dependency │ ├── data/ │ └── migration/ # SQL migrations │ ├── 001_initial.sql │ └── 002_add_index.sql │ └── examples/ └── config.toml # Example configuration ``` ## Examples in Tools Ecosystem Study these existing implementations: ### Simple: hello-tool - Basic project detection - Migration file discovery - Interactive selection example ```bash # View implementation cat /Users/Akasha/Tools/hello-tool/crates/hello-cli/src/main.rs ``` ### Advanced: syntaxis - Global `--project` flag - Project commands (list, show, detect, select) - Database migration handler - Multi-project global database ```bash # View implementation cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/main.rs ``` ### Migration Examples - Check how project-lifecycle uses migrations: ```bash cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/handlers/migrate_db.rs ``` ## Testing Integration Before publishing, verify: ```bash # Build your tool cargo build -p my-cli # Test project detection ./target/debug/my-tool project detect # Test configuration discovery ./target/debug/my-tool --config ./my-tool.toml # Test migration discovery ./target/debug/my-tool migrate --all . # Run full test suite cargo test --workspace ``` ## Common Issues ### Configuration Not Found **Problem**: `find_config_path()` returns `None` even though file exists **Solution**: - Check exact filename (case-sensitive) - Ensure file is in one of the search locations - Run from project root directory - Use `find_config_path_warn_conflicts()` to debug ### Project Not Detected **Problem**: `detect_project_from_cwd()` returns `None` **Solution**: - Verify `.project/lifecycle.toml` or `.vapora/lifecycle.toml` exists - Check current working directory - Run from project subdirectory (detection searches parents) - Use `detect_project_from_path()` with explicit path ### Interactive Selection Not Working **Problem**: `select_project()` shows no options **Solution**: - Ensure `features = ["interactive"]` in Cargo.toml - Pass non-empty project list - Check terminal supports ANSI (for colors/formatting) - Test with `--no-interactive` flag if available ### Migrations Not Found **Problem**: `find_migration_files()` returns empty vector **Solution**: - Create `data/migration/` directory - Add `.sql` files (lowercase extension) - Ensure files are readable - Run from correct directory or use absolute path ## Migration Guide ### From Local Database to Global If your tool previously used per-project databases: ```rust // Old way (per-project) let db_path = format!(".project/data/{}.db", project_name); let db = Database::open(&db_path)?; // New way (global, multi-project) let db_path = find_global_db_path("my-tool"); let db = Database::open(&db_path)?; // Store project context in data let projects = db.get_projects()?; let project = projects.iter().find(|p| p.name == project_name)?; let entries = db.get_entries(project.id)?; ``` ## Best Practices 1. **Always handle missing files gracefully** - Don't assume configuration exists 2. **Provide good error messages** - Tell users where to create config 3. **Cache project selection** - Use `save_last_project()` for quick re-runs 4. **Support explicit overrides** - Allow `--project` flag even when auto-detected 5. **Document search order** - Show users in help text where config is searched 6. **Use XDG directories** - Don't hardcode paths like `~/.myapp/` 7. **Support global database** - Use single DB for multi-project management 8. **Plan migrations** - Create `data/migration/` structure from start ## Getting Help - **Read source code**: `/Users/Akasha/Tools/shared/rust/` - **Check examples**: `/Users/Akasha/Tools/hello-tool/crates/hello-cli/src/main.rs` - **Study advanced usage**: `/Users/Akasha/Development/syntaxis/` - **Run tests**: `cargo test -p tools-shared` - **API docs**: `cargo doc -p tools-shared --open` ## Next Steps 1. Add shared dependency to your CLI 2. Follow "Quick Start" section above 3. Implement features incrementally 4. Test each feature thoroughly 5. Update documentation 6. Commit with clear commit message 7. Share with the team!