use clap::Args;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Args)]
pub struct DependencySpec {
#[arg(
value_name = "SPEC",
help = "Dependency spec: 'source:path@version' for Git (e.g., community:agents/helper.md@v1.0.0) or './path' for local files. Use --help for more examples"
)]
pub spec: String,
#[arg(long)]
pub name: Option<String>,
#[arg(long)]
pub tool: Option<String>,
#[arg(long)]
pub target: Option<String>,
#[arg(long)]
pub filename: Option<String>,
#[arg(long, short = 'f')]
pub force: bool,
#[arg(long)]
pub no_install: bool,
}
#[derive(Debug, Clone, Args)]
pub struct AgentDependency {
#[command(flatten)]
pub common: DependencySpec,
}
#[derive(Debug, Clone, Args)]
pub struct SnippetDependency {
#[command(flatten)]
pub common: DependencySpec,
}
#[derive(Debug, Clone, Args)]
pub struct CommandDependency {
#[command(flatten)]
pub common: DependencySpec,
}
#[derive(Debug, Clone, Args)]
pub struct McpServerDependency {
#[command(flatten)]
pub common: DependencySpec,
}
#[derive(Debug, Clone)]
pub enum DependencyType {
Agent(AgentDependency),
Snippet(SnippetDependency),
Command(CommandDependency),
Script(ScriptDependency),
Hook(HookDependency),
McpServer(McpServerDependency),
}
impl DependencyType {
#[must_use]
pub const fn common(&self) -> &DependencySpec {
match self {
Self::Agent(dep) => &dep.common,
Self::Snippet(dep) => &dep.common,
Self::Command(dep) => &dep.common,
Self::Script(dep) => &dep.common,
Self::Hook(dep) => &dep.common,
Self::McpServer(dep) => &dep.common,
}
}
#[must_use]
pub const fn resource_type(&self) -> &'static str {
match self {
Self::Agent(_) => "agent",
Self::Snippet(_) => "snippet",
Self::Command(_) => "command",
Self::Script(_) => "script",
Self::Hook(_) => "hook",
Self::McpServer(_) => "mcp-server",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceSpec {
pub name: String,
pub url: String,
}
#[derive(Debug, Clone, Default)]
pub struct InstallOptions {
pub no_install: bool,
pub force: bool,
pub quiet: bool,
pub offline: bool,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateOptions {
pub all: bool,
pub dependencies: Vec<String>,
pub breaking: bool,
pub quiet: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dependency_type_common() {
let agent = DependencyType::Agent(AgentDependency {
common: DependencySpec {
spec: "test:agent.md".to_string(),
name: None,
tool: None,
target: None,
filename: None,
force: false,
no_install: false,
},
});
assert_eq!(agent.common().spec, "test:agent.md");
assert_eq!(agent.resource_type(), "agent");
}
#[test]
fn test_mcp_server_dependency() {
let mcp = DependencyType::McpServer(McpServerDependency {
common: DependencySpec {
spec: "test:mcp.toml".to_string(),
name: Some("test-server".to_string()),
tool: None,
target: None,
filename: None,
force: true,
no_install: false,
},
});
assert_eq!(mcp.common().spec, "test:mcp.toml");
assert_eq!(mcp.common().name, Some("test-server".to_string()));
assert!(mcp.common().force);
assert_eq!(mcp.resource_type(), "mcp-server");
}
}
#[derive(Debug, Clone, Args)]
pub struct ScriptDependency {
#[command(flatten)]
pub common: DependencySpec,
}
#[derive(Debug, Clone, Args)]
pub struct HookDependency {
#[command(flatten)]
pub common: DependencySpec,
}