use crate::try_open_runtime;
use carryctx::application::runtime::InvocationContext;
use carryctx::error::ExitCode;
use clap::Parser;
#[derive(Parser, Debug)]
pub struct PresetArgs {
#[command(subcommand)]
pub command: PresetCommand,
}
#[derive(Parser, Debug)]
pub enum PresetCommand {
Install {
#[arg(index = 1)]
source: String,
},
Activate {
#[arg(index = 1)]
name: String,
},
Apply {
#[arg(index = 1)]
name: String,
},
Show {
#[arg(index = 1)]
name: String,
},
List,
}
pub fn handle_preset(
args: &PresetArgs,
ctx: &InvocationContext,
is_json: bool,
) -> Result<ExitCode, ExitCode> {
use carryctx::application::preset::PresetManager;
use std::path::Path;
let runtime = try_open_runtime(ctx)?;
let repo_root = runtime.git_project.repository_root.as_path();
let manager = PresetManager::new(repo_root);
match &args.command {
PresetCommand::Install { source } => {
let source_path = Path::new(source);
match manager.install_preset(source_path) {
Ok(entry) => {
if !is_json {
println!("✅ Successfully installed preset '{}'", entry.source);
println!(" Integrity Hash: {}", entry.integrity);
println!(
" Permissions: filesystem={}, network={}, env={}",
entry.permissions_granted.requires_filesystem,
entry.permissions_granted.requires_network,
entry.permissions_granted.requires_env.len()
);
println!("(Saved to .carryctx/presets.lock)");
} else {
println!(
r#"{{"schema_version":1,"command":"preset.install","success":true,"data":{{"status":"installed","name":"{}"}}}}"#,
source
);
}
Ok(ExitCode::Success)
}
Err(e) => {
if !is_json {
eprintln!("❌ Failed to install preset: {}", e);
} else {
println!(
r#"{{"schema_version":1,"command":"preset.install","success":false,"error":{{"message":"{}"}}}}"#,
e
);
}
Err(ExitCode::StateConflict)
}
}
}
PresetCommand::Activate { name } | PresetCommand::Apply { name } => {
match manager.activate_preset(name) {
Ok(entry) => {
if !is_json {
println!("✅ Activated preset '{}'", name);
println!(" Integrity Hash: {}", entry.integrity);
println!(" (Permissions validated against .carryctx/presets.lock)");
} else {
println!(
r#"{{"schema_version":1,"command":"preset.activate","success":true,"data":{{"status":"activated","name":"{}"}}}}"#,
name
);
}
Ok(ExitCode::Success)
}
Err(e) => {
if !is_json {
eprintln!("❌ Failed to activate preset '{}': {}", name, e);
} else {
println!(
r#"{{"schema_version":1,"command":"preset.activate","success":false,"error":{{"message":"{}"}}}}"#,
e
);
}
Err(ExitCode::StateConflict)
}
}
}
PresetCommand::Show { name } => {
let possible_paths = [
repo_root.join(".carryctx").join(format!("{}.md", name)),
repo_root.join(".carryctx").join(name),
runtime
.git_project
.git_common_dir
.parent()
.unwrap_or(repo_root)
.join(".carryctx")
.join(format!("{}.md", name)),
runtime
.git_project
.git_common_dir
.parent()
.unwrap_or(repo_root)
.join(".carryctx")
.join(name),
Path::new(name).to_path_buf(),
];
let found = possible_paths.iter().find(|p| p.exists() && p.is_file());
if let Some(path) = found {
match std::fs::read_to_string(path) {
Ok(content) => {
if !is_json {
println!("📄 Preset Spec: {}\n", path.display());
println!("{}", content);
} else {
println!(
r#"{{"schema_version":1,"command":"preset.show","success":true,"data":{{"path":"{}","content":{:?}}}}}"#,
path.display(),
content
);
}
Ok(ExitCode::Success)
}
Err(e) => {
eprintln!("❌ Failed to read preset file {}: {}", path.display(), e);
Err(ExitCode::General)
}
}
} else {
eprintln!("❌ Preset file '{}' not found in .carryctx/", name);
Err(ExitCode::General)
}
}
PresetCommand::List => match manager.read_lockfile() {
Ok(lockfile) => {
if !is_json {
println!("📦 Installed Presets (.carryctx/presets.lock):");
if lockfile.presets.is_empty() {
println!(" (No presets installed)");
} else {
for (name, entry) in &lockfile.presets {
println!(" - {} (v{})", name, entry.version);
println!(" Hash: {}", entry.integrity);
}
}
} else {
println!(
r#"{{"schema_version":1,"command":"preset.list","success":true,"data":[]}}"#
);
}
Ok(ExitCode::Success)
}
Err(e) => {
if !is_json {
eprintln!("❌ Failed to read lockfile: {}", e);
} else {
println!(
r#"{{"schema_version":1,"command":"preset.list","success":false,"error":{{"message":"{}"}}}}"#,
e
);
}
Err(ExitCode::Database)
}
},
}
}