Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
12 KiB
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):
[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
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:
use tools_shared::find_config_path;
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
config: Option<String>,
}
#[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:
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<String>,
#[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:
use tools_shared::{select_project, SelectableProject};
#[derive(Subcommand)]
enum Commands {
/// Select a project interactively
SelectProject,
}
async fn select_project_command(projects: Vec<ProjectEntity>) -> Result<String> {
let selectable: Vec<SelectableProject> = 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:
use tools_shared::{find_migration_files, run_migration_file};
#[derive(Subcommand)]
enum Commands {
/// Run migrations
Migrate {
/// Path to migration file
#[arg(long)]
file: Option<String>,
/// Find and run all migrations in data/migration/
#[arg(long)]
all: Option<String>,
},
}
async fn handle_migrate(file: Option<String>, all: Option<String>) -> 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:
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:
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-shareddependency - Update
Cargo.tomlhelp text to document search order - Use
find_config_path()to discover config files - Test configuration discovery from multiple locations
Project Detection
- Add global
--projectflag to CLI struct - Implement fallback to
detect_project_from_cwd() - Add
project detectcommand 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 --fileandmigrate --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
--projectflag 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
# View implementation
cat /Users/Akasha/Tools/hello-tool/crates/hello-cli/src/main.rs
Advanced: syntaxis
- Global
--projectflag - Project commands (list, show, detect, select)
- Database migration handler
- Multi-project global database
# View implementation
cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/main.rs
Migration Examples
- Check how project-lifecycle uses migrations:
cat /Users/Akasha/Development/syntaxis/crates/syntaxis-cli/src/handlers/migrate_db.rs
Testing Integration
Before publishing, verify:
# 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 <command>
# 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.tomlor.vapora/lifecycle.tomlexists - 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-interactiveflag if available
Migrations Not Found
Problem: find_migration_files() returns empty vector
Solution:
- Create
data/migration/directory - Add
.sqlfiles (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:
// 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
- Always handle missing files gracefully - Don't assume configuration exists
- Provide good error messages - Tell users where to create config
- Cache project selection - Use
save_last_project()for quick re-runs - Support explicit overrides - Allow
--projectflag even when auto-detected - Document search order - Show users in help text where config is searched
- Use XDG directories - Don't hardcode paths like
~/.myapp/ - Support global database - Use single DB for multi-project management
- 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
- Add shared dependency to your CLI
- Follow "Quick Start" section above
- Implement features incrementally
- Test each feature thoroughly
- Update documentation
- Commit with clear commit message
- Share with the team!