use std::path::PathBuf;
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Debug, Parser)]
#[command(
name = "skillpack",
bin_name = "skillpack",
version,
about = "Generate and verify the agent-distribution layer for any OSS project (Claude Code, Cursor, Codex, OpenCode, GitHub Copilot, AGENTS.md)."
)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(long, global = true)]
pub verbose: bool,
#[arg(long, global = true)]
pub debug: bool,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
Init {
#[arg(long, value_name = "DIR", default_value = ".")]
root: PathBuf,
#[arg(long)]
non_interactive: bool,
#[arg(long)]
accept_warnings: bool,
#[arg(long, value_name = "SPDX")]
license: Option<String>,
#[arg(long, num_args = 1.., value_name = "ECOSYSTEM")]
target: Vec<String>,
#[arg(long)]
force: bool,
},
Verify {
#[arg(long, value_name = "DIR", default_value = ".")]
root: PathBuf,
#[arg(long, value_enum, default_value_t = crate::verify::OutputFormat::Human)]
format: crate::verify::OutputFormat,
#[arg(long)]
fix: bool,
#[arg(long, value_name = "N", value_parser = clap::value_parser!(u8).range(0..=100))]
min_score: Option<u8>,
},
Doctor {
#[arg(long, value_name = "DIR", default_value = ".")]
root: PathBuf,
#[arg(long, value_enum, default_value_t = crate::verify::OutputFormat::Human)]
format: crate::verify::OutputFormat,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, clap::ValueEnum, Default)]
pub enum Target {
#[default]
Claude,
Cursor,
Codex,
#[clap(name = "opencode")]
OpenCode,
Copilot,
#[clap(name = "agentsmd")]
AgentsMd,
}
pub fn resolve_targets(raw: &[String]) -> anyhow::Result<Vec<Target>> {
let mut out = Vec::with_capacity(raw.len());
for r in raw {
if r == "all" {
out.extend([
Target::Claude,
Target::Cursor,
Target::Codex,
Target::OpenCode,
Target::Copilot,
Target::AgentsMd,
]);
} else {
out.push(Target::from_str(r, true).map_err(|s| {
anyhow::anyhow!(
"invalid --target `{s}`; expected claude|cursor|codex|opencode|copilot|agentsmd|all"
)
})?);
}
}
let mut seen = Vec::new();
for t in out {
if !seen.contains(&t) {
seen.push(t);
}
}
Ok(seen)
}