use std::env;
use std::fs;
use std::path::PathBuf;
use clap::{Args, Subcommand};
use miette::{IntoDiagnostic, WrapErr, miette};
use crate::failure::Failure;
use crate::output::OutputTarget;
const SKILL_MARKDOWN: &str = include_str!("../skill/SKILL.md");
const REFERENCE_MARKDOWN: &str = include_str!("../skill/reference.md");
const SKILL_FILES: [(&str, &str); 2] = [
("SKILL.md", SKILL_MARKDOWN),
("reference.md", REFERENCE_MARKDOWN),
];
const SKILL_DIRECTORY: &str = ".claude/skills/svugy";
#[derive(Args)]
pub struct SkillCommand {
#[command(subcommand)]
action: Option<SkillAction>,
}
#[derive(Subcommand)]
enum SkillAction {
Install(InstallSkill),
}
#[derive(Args)]
struct InstallSkill {
#[arg(long)]
user: bool,
}
impl SkillCommand {
pub fn run(self) -> Result<(), Failure> {
let Some(SkillAction::Install(install_skill)) = self.action else {
return OutputTarget::write_stdout(SKILL_MARKDOWN);
};
install_skill.run()
}
}
impl InstallSkill {
fn run(self) -> Result<(), Failure> {
let installation_root = self.installation_root()?;
let skill_directory = installation_root.join(SKILL_DIRECTORY);
let skill_directory_path = skill_directory.display();
fs::create_dir_all(&skill_directory)
.into_diagnostic()
.wrap_err_with(|| format!("cannot create {skill_directory_path}"))
.map_err(Failure::Io)?;
let mut written_paths = String::new();
for (file_name, contents) in SKILL_FILES {
let skill_file = skill_directory.join(file_name);
OutputTarget::write_file(&skill_file, contents)?;
let skill_file_path = skill_file.display();
let written_line = format!("wrote {skill_file_path}\n");
written_paths.push_str(&written_line);
}
OutputTarget::write_stdout(&written_paths)
}
fn installation_root(&self) -> Result<PathBuf, Failure> {
let is_user_installation = self.user;
if is_user_installation {
return env::home_dir()
.ok_or_else(|| miette!("cannot find the home directory"))
.map_err(Failure::Io);
}
env::current_dir()
.into_diagnostic()
.wrap_err_with(|| "cannot read the current directory")
.map_err(Failure::Io)
}
}