pub mod command;
pub mod templates;
use anyhow::{Context, Result};
use std::fs;
use std::path::{Path, PathBuf};
use crate::init::command::InitCommand;
use crate::subprocess::SubprocessManager;
async fn is_git_repository(path: &Path, subprocess: &SubprocessManager) -> bool {
if path.join(".git").exists() {
return true;
}
use crate::subprocess::ProcessCommandBuilder;
#[cfg(test)]
let command = ProcessCommandBuilder::new("git")
.args(["rev-parse", "--git-dir"])
.current_dir(path)
.suppress_stderr()
.build();
#[cfg(not(test))]
let command = ProcessCommandBuilder::new("git")
.args(["rev-parse", "--git-dir"])
.current_dir(path)
.build();
subprocess
.runner()
.run(command)
.await
.map(|output| output.status.success())
.unwrap_or(false)
}
async fn is_git_installed(subprocess: &SubprocessManager) -> bool {
use crate::subprocess::ProcessCommandBuilder;
let command = ProcessCommandBuilder::new("git").arg("--version").build();
subprocess
.runner()
.run(command)
.await
.map(|output| output.status.success())
.unwrap_or(false)
}
async fn initialize_git_repository(path: &Path, subprocess: &SubprocessManager) -> Result<()> {
use crate::subprocess::ProcessCommandBuilder;
let command = ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(path)
.build();
let output = subprocess
.runner()
.run(command)
.await
.context("Failed to run git init")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(output.stderr.as_bytes());
anyhow::bail!("Failed to initialize git repository: {}", stderr);
}
Ok(())
}
fn select_templates(cmd: &InitCommand) -> Result<Vec<templates::CommandTemplate>> {
if let Some(ref command_names) = cmd.commands {
if command_names.is_empty() {
println!("⚠️ No commands specified. Use --commands or omit to install all.");
return Ok(vec![]);
}
let selected = templates::get_templates_by_names(command_names);
if selected.is_empty() {
println!("❌ No matching commands found for: {command_names:?}");
println!(" Available commands: prodigy-code-review, prodigy-implement-spec, prodigy-lint, prodigy-product-enhance, prodigy-merge-worktree, prodigy-cleanup-tech-debt");
return Ok(vec![]);
}
Ok(selected)
} else {
Ok(templates::get_all_templates())
}
}
fn command_exists(commands_dir: &Path, template_name: &str) -> bool {
commands_dir.join(format!("{}.md", template_name)).exists()
}
fn find_existing_commands<'a>(
commands_dir: &Path,
templates: &'a [templates::CommandTemplate],
) -> Vec<&'a str> {
templates
.iter()
.filter(|t| command_exists(commands_dir, t.name))
.map(|t| t.name)
.collect()
}
fn is_test_environment() -> bool {
std::env::var("CARGO_TARGET_TMPDIR").is_ok()
|| std::env::var("RUST_TEST_THREADS").is_ok()
|| cfg!(test)
}
fn display_existing_commands_warning(existing: &[&str]) {
println!("\n⚠️ The following commands already exist:");
for name in existing {
println!(" - {name}");
}
println!(
"\nUse --force to overwrite existing commands, or --commands to select specific ones."
);
println!("Example: prodigy init --commands prodigy-lint,prodigy-product-enhance");
}
fn get_user_confirmation() -> Result<bool> {
use std::io::{self, IsTerminal, Write};
if std::io::stdin().is_terminal() && !is_test_environment() {
print!("\nDo you want to continue and skip existing commands? (y/N): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
if input.trim().to_lowercase() != "y" {
println!("❌ Installation cancelled.");
return Ok(false);
}
} else {
println!("ℹ️ Skipping existing commands (non-interactive mode).");
}
Ok(true)
}
#[cfg(test)]
fn should_process_templates(templates: &[templates::CommandTemplate]) -> bool {
!templates.is_empty()
}
#[cfg(test)]
fn needs_user_confirmation(existing_commands: &[&str]) -> bool {
!existing_commands.is_empty()
}
#[allow(dead_code)]
fn should_proceed_with_installation(existing_commands: &[&str]) -> bool {
existing_commands.is_empty()
}
#[cfg(test)]
fn validate_installation_preconditions(templates: &[templates::CommandTemplate]) -> bool {
!templates.is_empty()
}
#[cfg(test)]
fn process_existing_commands_check(existing: Vec<&str>) -> Result<bool> {
if !needs_user_confirmation(&existing) {
return Ok(true);
}
display_existing_commands_warning(&existing);
get_user_confirmation()
}
#[cfg(test)]
fn process_existing_commands_pipeline(existing: &[&str]) -> Result<bool> {
if existing.is_empty() {
return Ok(true);
}
display_existing_commands_warning(existing);
get_user_confirmation()
}
fn should_proceed_with_existing(existing: &[&str]) -> Result<bool> {
if existing.is_empty() {
Ok(true)
} else {
display_existing_commands_warning(existing);
get_user_confirmation()
}
}
fn handle_existing_commands(
commands_dir: &Path,
templates: &[templates::CommandTemplate],
) -> Result<bool> {
match templates.is_empty() {
true => Ok(true),
false => {
let existing = find_existing_commands(commands_dir, templates);
should_proceed_with_existing(&existing)
}
}
}
fn install_templates(
commands_dir: &Path,
templates: &[templates::CommandTemplate],
force: bool,
) -> Result<(usize, usize)> {
println!("\n📦 Installing {} command(s)...", templates.len());
let mut installed = 0;
let mut skipped = 0;
for template in templates {
match install_command(commands_dir, template, force) {
Ok(_) => installed += 1,
Err(e) => {
eprintln!("❌ Failed to install '{}': {}", template.name, e);
skipped += 1;
}
}
}
Ok((installed, skipped))
}
fn display_installation_summary(installed: usize, skipped: usize, commands_dir: &Path) {
println!("\n✨ Installation complete!");
println!(" - {installed} command(s) installed");
if skipped > 0 {
println!(" - {skipped} command(s) skipped");
}
if installed > 0 {
println!("\n📚 Next steps:");
println!(
" 1. Review installed commands in: {}",
commands_dir.display()
);
println!(" 2. Customize commands as needed for your project");
println!(" 3. Run 'prodigy cook' to start improving your code");
println!("\n💡 Tip: You can always reinstall default commands with 'prodigy init --force'");
}
}
fn install_command(
commands_dir: &Path,
template: &templates::CommandTemplate,
force: bool,
) -> Result<()> {
let file_path = commands_dir.join(format!("{}.md", template.name));
if file_path.exists() && !force {
println!(
"⚠️ Command '{}' already exists. Use --force to overwrite.",
template.name
);
return Ok(());
}
fs::write(&file_path, template.content)
.with_context(|| format!("Failed to write command file: {}", file_path.display()))?;
println!("✅ Installed command: {}", template.name);
Ok(())
}
async fn validate_project_structure(
cmd: &InitCommand,
subprocess: &SubprocessManager,
) -> Result<(PathBuf, PathBuf)> {
let target_dir = cmd.path.clone().unwrap_or_else(|| PathBuf::from("."));
let target_dir = target_dir
.canonicalize()
.with_context(|| format!("Failed to resolve path: {}", target_dir.display()))?;
println!(
"🚀 Initializing Prodigy commands in: {}",
target_dir.display()
);
if !is_git_repository(&target_dir, subprocess).await {
if !is_git_installed(subprocess).await {
anyhow::bail!(
"Error: git is not installed on your system.\n\
Prodigy requires git to manage workflow history.\n\
Please install git and try again."
);
}
println!("📦 Directory is not a git repository. Initializing git...");
initialize_git_repository(&target_dir, subprocess).await?;
println!("✅ Git repository initialized successfully.");
}
let commands_dir = initialize_directories(&target_dir)?;
Ok((target_dir, commands_dir))
}
fn initialize_directories(target_dir: &Path) -> Result<PathBuf> {
let claude_dir = target_dir.join(".claude");
let commands_dir = claude_dir.join("commands");
if !commands_dir.exists() {
fs::create_dir_all(&commands_dir)
.with_context(|| format!("Failed to create directory: {}", commands_dir.display()))?;
println!("📁 Created directory: {}", commands_dir.display());
}
Ok(commands_dir)
}
pub async fn run(cmd: InitCommand) -> Result<()> {
let subprocess = SubprocessManager::production();
let (_target_dir, commands_dir) = validate_project_structure(&cmd, &subprocess).await?;
let templates = select_templates(&cmd)?;
if !cmd.force && !handle_existing_commands(&commands_dir, &templates)? {
return Ok(());
}
let (installed, skipped) = install_templates(&commands_dir, &templates, cmd.force)?;
display_installation_summary(installed, skipped, &commands_dir);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::init::command::InitCommand;
use tempfile::TempDir;
#[tokio::test]
async fn test_is_git_repository() {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path();
let subprocess = SubprocessManager::production();
assert!(!is_git_repository(path, &subprocess).await);
use crate::subprocess::ProcessCommandBuilder;
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(path)
.build(),
)
.await
.unwrap();
assert!(is_git_repository(path, &subprocess).await);
}
#[tokio::test]
async fn test_is_git_installed() {
let subprocess = SubprocessManager::production();
assert!(is_git_installed(&subprocess).await);
}
#[tokio::test]
async fn test_initialize_git_repository() {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path();
let subprocess = SubprocessManager::production();
assert!(!is_git_repository(path, &subprocess).await);
let result = initialize_git_repository(path, &subprocess).await;
assert!(result.is_ok());
assert!(is_git_repository(path, &subprocess).await);
assert!(path.join(".git").exists());
}
#[test]
fn test_get_templates() {
let all_templates = templates::get_all_templates();
assert!(all_templates.len() >= 6);
let names = vec![
"prodigy-lint".to_string(),
"prodigy-code-review".to_string(),
];
let filtered = templates::get_templates_by_names(&names);
assert_eq!(filtered.len(), 2);
}
#[tokio::test]
async fn test_run_init_not_git_repo_auto_init() {
let temp_dir = TempDir::new().unwrap();
let cmd = InitCommand {
force: false,
commands: None,
path: Some(temp_dir.path().to_path_buf()),
};
let result = run(cmd).await;
assert!(result.is_ok());
assert!(temp_dir.path().join(".git").exists());
let commands_dir = temp_dir.path().join(".claude").join("commands");
assert!(commands_dir.exists());
}
#[tokio::test]
async fn test_run_init_create_commands() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
let cmd = InitCommand {
force: false,
commands: None,
path: Some(temp_dir.path().to_path_buf()),
};
let result = run(cmd).await;
assert!(result.is_ok());
let commands_dir = temp_dir.path().join(".claude").join("commands");
assert!(commands_dir.exists());
assert!(commands_dir.join("prodigy-code-review.md").exists());
assert!(commands_dir.join("prodigy-lint.md").exists());
}
#[test]
fn test_should_process_templates() {
let templates: Vec<templates::CommandTemplate> = vec![];
assert!(!should_process_templates(&templates));
let templates = templates::get_all_templates();
assert!(should_process_templates(&templates));
}
#[test]
fn test_needs_user_confirmation() {
let existing: Vec<&str> = vec![];
assert!(!needs_user_confirmation(&existing));
let existing = vec!["prodigy-lint", "prodigy-code-review"];
assert!(needs_user_confirmation(&existing));
}
#[test]
fn test_process_existing_commands_check_no_existing() {
let existing: Vec<&str> = vec![];
let result = process_existing_commands_check(existing);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_process_existing_commands_check_with_existing() {
let existing = vec!["prodigy-lint", "prodigy-code-review"];
let result = process_existing_commands_check(existing);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_refactored_handle_existing_commands_empty() {
let temp_dir = TempDir::new().unwrap();
let templates: Vec<templates::CommandTemplate> = vec![];
let result = handle_existing_commands(temp_dir.path(), &templates);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_refactored_handle_existing_commands_no_conflicts() {
let temp_dir = TempDir::new().unwrap();
let templates = templates::get_all_templates();
let result = handle_existing_commands(temp_dir.path(), &templates);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[tokio::test]
async fn test_run_init_with_existing_commands() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
let commands_dir = temp_dir.path().join(".claude").join("commands");
std::fs::create_dir_all(&commands_dir).unwrap();
std::fs::write(
commands_dir.join("prodigy-code-review.md"),
"existing content",
)
.unwrap();
let cmd = InitCommand {
force: false,
commands: None,
path: Some(temp_dir.path().to_path_buf()),
};
let result = run(cmd).await;
assert!(result.is_ok());
let content = std::fs::read_to_string(commands_dir.join("prodigy-code-review.md")).unwrap();
assert_eq!(content, "existing content");
}
#[tokio::test]
async fn test_run_init_force_overwrite() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
let commands_dir = temp_dir.path().join(".claude").join("commands");
std::fs::create_dir_all(&commands_dir).unwrap();
std::fs::write(commands_dir.join("prodigy-code-review.md"), "old content").unwrap();
let cmd = InitCommand {
force: true,
commands: None,
path: Some(temp_dir.path().to_path_buf()),
};
let result = run(cmd).await;
assert!(result.is_ok());
let content = std::fs::read_to_string(commands_dir.join("prodigy-code-review.md")).unwrap();
assert!(content.contains("Analyze code"));
assert!(!content.contains("old content"));
}
#[tokio::test]
async fn test_run_init_specific_commands() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
let cmd = InitCommand {
force: false,
commands: Some(vec![
"prodigy-code-review".to_string(),
"prodigy-lint".to_string(),
]),
path: Some(temp_dir.path().to_path_buf()),
};
let result = run(cmd).await;
assert!(result.is_ok());
let commands_dir = temp_dir.path().join(".claude").join("commands");
assert!(commands_dir.join("prodigy-code-review.md").exists());
assert!(commands_dir.join("prodigy-lint.md").exists());
assert!(!commands_dir.join("prodigy-implement-spec.md").exists());
}
#[test]
fn test_command_exists() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
fs::write(commands_dir.join("existing.md"), "content").unwrap();
assert!(command_exists(&commands_dir, "existing"));
assert!(!command_exists(&commands_dir, "non-existing"));
}
#[test]
fn test_find_existing_commands() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
fs::write(commands_dir.join("command1.md"), "content").unwrap();
fs::write(commands_dir.join("command3.md"), "content").unwrap();
let templates = vec![
templates::CommandTemplate {
name: "command1",
content: "content1",
description: "Command 1",
},
templates::CommandTemplate {
name: "command2",
content: "content2",
description: "Command 2",
},
templates::CommandTemplate {
name: "command3",
content: "content3",
description: "Command 3",
},
];
let existing = find_existing_commands(&commands_dir, &templates);
assert_eq!(existing.len(), 2);
assert!(existing.contains(&"command1"));
assert!(existing.contains(&"command3"));
assert!(!existing.contains(&"command2"));
}
#[test]
fn test_find_existing_commands_empty() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
let templates = vec![templates::CommandTemplate {
name: "command1",
content: "content1",
description: "Command 1",
}];
let existing = find_existing_commands(&commands_dir, &templates);
assert_eq!(existing.len(), 0);
}
#[test]
fn test_is_test_environment() {
assert!(is_test_environment());
}
#[test]
fn test_handle_existing_commands_no_tty() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
let templates = vec![templates::CommandTemplate {
name: "test-command",
content: "#!/bin/bash\necho test",
description: "Test command",
}];
let result = handle_existing_commands(&commands_dir, &templates).unwrap();
assert!(result);
}
#[test]
fn test_handle_existing_commands_empty_templates() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
let templates = vec![];
let result = handle_existing_commands(&commands_dir, &templates).unwrap();
assert!(result);
}
#[test]
fn test_handle_existing_commands_no_conflicts() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
let templates = vec![templates::CommandTemplate {
name: "new-command",
content: "content",
description: "New command",
}];
let result = handle_existing_commands(&commands_dir, &templates).unwrap();
assert!(result);
}
#[test]
fn test_handle_existing_commands_with_conflicts() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
fs::write(commands_dir.join("test-command.md"), "existing content").unwrap();
let templates = vec![templates::CommandTemplate {
name: "test-command",
content: "new content",
description: "Test command",
}];
let result = handle_existing_commands(&commands_dir, &templates);
assert!(result.is_ok());
}
#[test]
fn test_handle_existing_commands_multiple_conflicts() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
fs::write(commands_dir.join("test-command1.md"), "existing content 1").unwrap();
fs::write(commands_dir.join("test-command2.md"), "existing content 2").unwrap();
fs::write(commands_dir.join("test-command3.md"), "existing content 3").unwrap();
let templates = vec![
templates::CommandTemplate {
name: "test-command1",
content: "new content 1",
description: "Test command 1",
},
templates::CommandTemplate {
name: "test-command2",
content: "new content 2",
description: "Test command 2",
},
templates::CommandTemplate {
name: "test-command3",
content: "new content 3",
description: "Test command 3",
},
templates::CommandTemplate {
name: "test-command4",
content: "new content 4",
description: "Test command 4",
},
];
let result = handle_existing_commands(&commands_dir, &templates);
assert!(result.is_ok());
assert!(result.unwrap()); }
#[test]
fn test_display_existing_commands_warning() {
let existing = vec!["cmd1", "cmd2", "cmd3"];
display_existing_commands_warning(&existing);
}
#[test]
fn test_display_existing_commands_warning_empty() {
let existing: Vec<&str> = vec![];
display_existing_commands_warning(&existing);
}
#[test]
fn test_display_existing_commands_warning_single() {
let existing = vec!["single-command"];
display_existing_commands_warning(&existing);
}
#[test]
fn test_get_user_confirmation_non_tty() {
let result = get_user_confirmation();
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_get_user_confirmation_test_env() {
let result = get_user_confirmation();
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_handle_existing_commands_partial_conflicts() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
fs::write(commands_dir.join("existing-cmd.md"), "existing").unwrap();
let templates = vec![
templates::CommandTemplate {
name: "existing-cmd",
content: "new content",
description: "Existing command",
},
templates::CommandTemplate {
name: "new-cmd",
content: "new content",
description: "New command",
},
];
let result = handle_existing_commands(&commands_dir, &templates);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_should_proceed_with_existing_empty() {
let existing: Vec<&str> = vec![];
let result = should_proceed_with_existing(&existing).unwrap();
assert!(result);
}
#[test]
fn test_should_proceed_with_existing_non_empty() {
let existing = vec!["command1", "command2"];
let result = should_proceed_with_existing(&existing);
assert!(result.is_ok());
}
#[test]
fn test_handle_existing_commands_pattern_matching() {
let temp_dir = TempDir::new().unwrap();
let commands_dir = temp_dir.path().join("commands");
fs::create_dir_all(&commands_dir).unwrap();
let empty_templates = vec![];
let result = handle_existing_commands(&commands_dir, &empty_templates).unwrap();
assert!(result);
let templates = vec![templates::CommandTemplate {
name: "test-cmd",
content: "content",
description: "desc",
}];
let result = handle_existing_commands(&commands_dir, &templates).unwrap();
assert!(result);
}
#[tokio::test]
async fn test_validate_project_structure_not_git_repo() {
let temp_dir = TempDir::new().unwrap();
let cmd = InitCommand {
path: Some(temp_dir.path().to_path_buf()),
commands: None,
force: false,
};
let subprocess = SubprocessManager::production();
let result = validate_project_structure(&cmd, &subprocess).await;
assert!(result.is_ok());
assert!(temp_dir.path().join(".git").exists());
}
#[tokio::test]
async fn test_validate_project_structure_with_symlinks() {
let temp_dir = TempDir::new().unwrap();
let real_path = temp_dir.path().join("real");
let symlink_path = temp_dir.path().join("symlink");
fs::create_dir_all(&real_path).unwrap();
#[cfg(unix)]
std::os::unix::fs::symlink(&real_path, &symlink_path).unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(&real_path)
.build(),
)
.await
.unwrap();
let cmd = InitCommand {
path: Some(symlink_path),
commands: None,
force: false,
};
#[cfg(unix)]
{
let result = validate_project_structure(&cmd, &subprocess).await;
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_init_run_success() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
let args = InitCommand {
path: Some(temp_dir.path().to_path_buf()),
commands: None,
force: false,
};
let result = run(args).await;
assert!(result.is_ok());
assert!(temp_dir.path().join(".claude").exists());
}
#[tokio::test]
async fn test_init_run_already_initialized() {
let temp_dir = TempDir::new().unwrap();
use crate::subprocess::ProcessCommandBuilder;
let subprocess = SubprocessManager::production();
subprocess
.runner()
.run(
ProcessCommandBuilder::new("git")
.arg("init")
.current_dir(temp_dir.path())
.build(),
)
.await
.unwrap();
fs::create_dir(temp_dir.path().join(".claude")).unwrap();
let args = InitCommand {
path: Some(temp_dir.path().to_path_buf()),
commands: None,
force: false,
};
let result = run(args).await;
assert!(result.is_ok()); }
#[test]
fn test_should_proceed_with_installation() {
let empty_existing: Vec<&str> = vec![];
assert!(should_proceed_with_installation(&empty_existing));
let existing = vec!["prodigy-lint", "prodigy-review"];
assert!(!should_proceed_with_installation(&existing));
}
#[test]
fn test_validate_installation_preconditions() {
use crate::init::templates::CommandTemplate;
let empty_templates: Vec<CommandTemplate> = vec![];
assert!(!validate_installation_preconditions(&empty_templates));
let templates = vec![CommandTemplate {
name: "test-command",
description: "Test command",
content: "test content",
}];
assert!(validate_installation_preconditions(&templates));
}
#[test]
fn test_process_existing_commands_pipeline_empty() {
let empty_existing: Vec<&str> = vec![];
let result = process_existing_commands_pipeline(&empty_existing);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_process_existing_commands_pipeline_with_items() {
let existing = vec!["cmd1", "cmd2"];
let result = process_existing_commands_pipeline(&existing);
assert!(result.is_ok());
}
}