| |
| |
| |
| |
| |
| |
|
|
| use std::path::PathBuf; |
|
|
| use clap::{Parser, Subcommand, ValueEnum}; |
| use forge_domain::{AgentId, ConversationId, Effort, ModelId, ProviderId}; |
|
|
| #[derive(Parser)] |
| #[command(version = env!("CARGO_PKG_VERSION"))] |
| pub struct Cli { |
| |
| |
| |
| |
| |
| #[arg(long, short = 'p', allow_hyphen_values = true)] |
| pub prompt: Option<String>, |
|
|
| |
| |
| |
| |
| |
| #[arg(skip)] |
| pub piped_input: Option<String>, |
|
|
| |
| #[arg(long)] |
| pub conversation: Option<PathBuf>, |
|
|
| |
| |
| |
| |
| #[arg(long, alias = "cid")] |
| pub conversation_id: Option<ConversationId>, |
|
|
| |
| |
| |
| #[arg(long, short = 'C')] |
| pub directory: Option<PathBuf>, |
|
|
| |
| #[arg(long)] |
| pub sandbox: Option<String>, |
|
|
| |
| #[arg(long, default_value_t = false)] |
| pub verbose: bool, |
|
|
| |
| #[arg(long, alias = "aid")] |
| pub agent: Option<AgentId>, |
|
|
| |
| #[command(subcommand)] |
| pub subcommands: Option<TopLevelCommand>, |
|
|
| |
| #[arg(long, short = 'e')] |
| pub event: Option<String>, |
| } |
|
|
| impl Cli { |
| |
| |
| |
| |
| pub fn is_interactive(&self) -> bool { |
| self.prompt.is_none() && self.piped_input.is_none() && self.subcommands.is_none() |
| } |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum TopLevelCommand { |
| |
| Agent(AgentCommandGroup), |
|
|
| |
| #[command(subcommand, alias = "extension")] |
| Zsh(ZshCommandGroup), |
|
|
| |
| List(ListCommandGroup), |
|
|
| |
| Banner, |
|
|
| |
| Info { |
| |
| #[arg(long, alias = "cid")] |
| conversation_id: Option<ConversationId>, |
|
|
| |
| #[arg(long)] |
| porcelain: bool, |
| }, |
|
|
| |
| Config(ConfigCommandGroup), |
|
|
| |
| #[command(alias = "session")] |
| Conversation(ConversationCommandGroup), |
|
|
| |
| Commit(CommitCommandGroup), |
|
|
| |
| Mcp(McpCommandGroup), |
|
|
| |
| Suggest { |
| |
| #[arg(allow_hyphen_values = true)] |
| prompt: String, |
| }, |
|
|
| |
| Provider(ProviderCommandGroup), |
|
|
| |
| #[command(aliases = ["command", "commands"])] |
| Cmd(CmdCommandGroup), |
|
|
| |
| Workspace(WorkspaceCommandGroup), |
|
|
| |
| Data(DataCommandGroup), |
|
|
| |
| #[command(subcommand)] |
| Vscode(VscodeCommand), |
|
|
| |
| Update(UpdateArgs), |
|
|
| |
| |
| Setup, |
|
|
| |
| Doctor, |
|
|
| |
| Logs(LogsArgs), |
|
|
| |
| Select(SelectCommandGroup), |
| } |
|
|
| |
| |
| |
| |
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct SelectCommandGroup { |
| #[command(subcommand)] |
| pub command: SelectCommand, |
| } |
|
|
| |
| |
| |
| |
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum SelectCommand { |
| |
| |
| |
| |
| Model { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
| }, |
|
|
| |
| |
| |
| |
| Agent { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
| }, |
|
|
| |
| |
| |
| |
| Provider { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
|
|
| |
| #[arg(long)] |
| configured: bool, |
| }, |
|
|
| |
| |
| |
| |
| ReasoningEffort { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
| }, |
|
|
| |
| |
| |
| |
| Command { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
| }, |
|
|
| |
| |
| |
| |
| Conversation { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
|
|
| |
| #[arg(long)] |
| parent: Option<ConversationId>, |
| }, |
|
|
| |
| |
| |
| |
| |
| File { |
| |
| #[arg(long, short = 'q')] |
| query: Option<String>, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct CmdCommandGroup { |
| #[command(subcommand)] |
| pub command: CmdCommand, |
|
|
| |
| #[arg(long, alias = "cid", global = true)] |
| pub conversation_id: Option<ConversationId>, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum CmdCommand { |
| |
| List { |
| |
| #[arg(long)] |
| custom: bool, |
| }, |
|
|
| |
| Execute { |
| |
| commands: Vec<String>, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct AgentCommandGroup { |
| #[command(subcommand)] |
| pub command: AgentCommand, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum AgentCommand { |
| |
| #[command(alias = "ls")] |
| List, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct WorkspaceCommandGroup { |
| #[command(subcommand)] |
| pub command: WorkspaceCommand, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum WorkspaceCommand { |
| |
| Sync { |
| |
| #[arg(default_value = ".")] |
| path: PathBuf, |
|
|
| |
| |
| #[arg(long)] |
| init: bool, |
| }, |
| |
| List { |
| |
| #[arg(short, long)] |
| porcelain: bool, |
| }, |
|
|
| |
| Query { |
| |
| query: String, |
|
|
| |
| |
| #[arg(default_value = ".")] |
| path: PathBuf, |
|
|
| |
| #[arg(short, long, default_value = "10")] |
| limit: usize, |
|
|
| |
| #[arg(long)] |
| top_k: Option<u32>, |
|
|
| |
| #[arg(long, short = 'r')] |
| use_case: String, |
|
|
| |
| #[arg(long)] |
| starts_with: Option<String>, |
|
|
| |
| #[arg(long)] |
| ends_with: Option<String>, |
| }, |
|
|
| |
| Info { |
| |
| #[arg(default_value = ".")] |
| path: PathBuf, |
| }, |
|
|
| |
| Delete { |
| |
| workspace_ids: Vec<String>, |
| }, |
|
|
| |
| Status { |
| |
| #[arg(default_value = ".")] |
| path: PathBuf, |
|
|
| |
| #[arg(short, long)] |
| porcelain: bool, |
| }, |
|
|
| |
| Init { |
| |
| #[arg(default_value = ".")] |
| path: PathBuf, |
|
|
| |
| #[arg(short = 'y', long)] |
| yes: bool, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ListCommandGroup { |
| #[command(subcommand)] |
| pub command: ListCommand, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ListCommand { |
| |
| #[command(alias = "agents")] |
| Agent { |
| |
| #[arg(long)] |
| custom: bool, |
| }, |
|
|
| |
| #[command(alias = "providers")] |
| Provider { |
| |
| |
| #[arg(long = "type", short = 't')] |
| types: Vec<forge_domain::ProviderType>, |
| }, |
|
|
| |
| #[command(alias = "models")] |
| Model, |
|
|
| |
| #[command(hide = true, alias = "commands")] |
| Command { |
| |
| #[arg(long)] |
| custom: bool, |
| }, |
|
|
| |
| #[command(alias = "configs")] |
| Config, |
|
|
| |
| #[command(alias = "tools")] |
| Tool { |
| |
| agent: AgentId, |
| }, |
|
|
| |
| #[command(alias = "mcps")] |
| Mcp, |
|
|
| |
| #[command(alias = "session")] |
| Conversation { |
| |
| #[arg(long)] |
| parent: Option<ConversationId>, |
| }, |
|
|
| |
| #[command(alias = "cmds")] |
| Cmd, |
|
|
| |
| #[command(alias = "skills")] |
| Skill { |
| |
| #[arg(long)] |
| custom: bool, |
| }, |
|
|
| |
| |
| |
| |
| #[command(alias = "files")] |
| File, |
| } |
|
|
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ZshCommandGroup { |
| |
| Plugin, |
| |
| Theme, |
| |
| Doctor, |
|
|
| |
| |
| Rprompt, |
|
|
| |
| Setup, |
|
|
| |
| Keyboard, |
|
|
| |
| |
| |
| |
| Format { |
| |
| #[arg(long)] |
| buffer: String, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct McpCommandGroup { |
| #[command(subcommand)] |
| pub command: McpCommand, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum McpCommand { |
| |
| Import(McpImportArgs), |
|
|
| |
| List, |
|
|
| |
| Remove(McpRemoveArgs), |
|
|
| |
| Show(McpShowArgs), |
|
|
| |
| Reload, |
|
|
| |
| Login(McpAuthArgs), |
|
|
| |
| Logout(McpLogoutArgs), |
| } |
|
|
| #[derive(Parser, Debug, Clone)] |
| pub struct McpImportArgs { |
| |
| #[arg()] |
| pub json: String, |
|
|
| |
| #[arg(short = 's', long = "scope", default_value = "local")] |
| pub scope: Scope, |
| } |
|
|
| #[derive(Parser, Debug, Clone)] |
| pub struct McpRemoveArgs { |
| |
| #[arg(short = 's', long = "scope", default_value = "local")] |
| pub scope: Scope, |
|
|
| |
| pub name: String, |
| } |
|
|
| #[derive(Parser, Debug, Clone)] |
| pub struct McpShowArgs { |
| |
| pub name: String, |
| } |
|
|
| #[derive(Parser, Debug, Clone)] |
| pub struct McpAuthArgs { |
| |
| pub name: String, |
| } |
|
|
| #[derive(Parser, Debug, Clone)] |
| pub struct McpLogoutArgs { |
| |
| |
| pub name: String, |
| } |
|
|
| |
| #[derive(Copy, Clone, Debug, ValueEnum, Default)] |
| pub enum Scope { |
| |
| #[default] |
| Local, |
| |
| User, |
| } |
|
|
| impl From<Scope> for forge_domain::Scope { |
| fn from(value: Scope) -> Self { |
| match value { |
| Scope::Local => forge_domain::Scope::Local, |
| Scope::User => forge_domain::Scope::User, |
| } |
| } |
| } |
|
|
| |
| #[derive(Copy, Clone, Debug, ValueEnum)] |
| #[clap(rename_all = "lower")] |
| pub enum Transport { |
| |
| Stdio, |
| |
| Sse, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ConfigCommandGroup { |
| #[command(subcommand)] |
| pub command: ConfigCommand, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ConfigCommand { |
| |
| Set(ConfigSetArgs), |
|
|
| |
| Get(ConfigGetArgs), |
|
|
| |
| List, |
|
|
| |
| Path, |
|
|
| |
| Migrate, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ConfigSetArgs { |
| #[command(subcommand)] |
| pub field: ConfigSetField, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ConfigGetArgs { |
| #[command(subcommand)] |
| pub field: ConfigGetField, |
| } |
|
|
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ConfigSetField { |
| |
| Model { |
| |
| provider: ProviderId, |
| |
| model: ModelId, |
| }, |
| |
| Commit { |
| |
| provider: ProviderId, |
| |
| model: ModelId, |
| }, |
| |
| Suggest { |
| |
| provider: ProviderId, |
| |
| model: ModelId, |
| }, |
| |
| ReasoningEffort { |
| |
| effort: Effort, |
| }, |
| } |
|
|
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ConfigGetField { |
| |
| Model, |
| |
| Provider, |
| |
| Commit, |
| |
| Suggest, |
| |
| ReasoningEffort, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ConversationCommandGroup { |
| #[command(subcommand)] |
| pub command: ConversationCommand, |
| } |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ConversationCommand { |
| |
| List { |
| |
| #[arg(long)] |
| porcelain: bool, |
| }, |
|
|
| |
| New, |
|
|
| |
| Dump { |
| |
| id: ConversationId, |
|
|
| |
| #[arg(long)] |
| html: bool, |
| }, |
|
|
| |
| Compact { |
| |
| id: ConversationId, |
| }, |
|
|
| |
| Retry { |
| |
| id: ConversationId, |
| }, |
|
|
| |
| Resume { |
| |
| id: ConversationId, |
| }, |
|
|
| |
| Show { |
| |
| id: ConversationId, |
|
|
| |
| #[arg(long)] |
| md: bool, |
| }, |
|
|
| |
| Info { |
| |
| id: ConversationId, |
| }, |
|
|
| |
| Stats { |
| |
| id: ConversationId, |
|
|
| |
| #[arg(long)] |
| porcelain: bool, |
| }, |
|
|
| |
| Clone { |
| |
| id: ConversationId, |
|
|
| |
| #[arg(long)] |
| porcelain: bool, |
| }, |
|
|
| |
| Delete { |
| |
| id: String, |
| }, |
|
|
| |
| Rename { |
| |
| id: ConversationId, |
|
|
| |
| name: String, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct ProviderCommandGroup { |
| #[command(subcommand)] |
| pub command: ProviderCommand, |
|
|
| |
| #[arg(long, global = true)] |
| pub porcelain: bool, |
| } |
|
|
| #[derive(Subcommand, Debug, Clone)] |
| pub enum ProviderCommand { |
| |
| |
| |
| Login { |
| |
| provider: Option<ProviderId>, |
| }, |
|
|
| |
| |
| |
| Logout { |
| |
| provider: Option<ProviderId>, |
| }, |
|
|
| |
| List { |
| |
| |
| #[arg(long = "type", short = 't')] |
| types: Vec<forge_domain::ProviderType>, |
| }, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct CommitCommandGroup { |
| |
| #[arg(long)] |
| pub preview: bool, |
|
|
| |
| |
| |
| |
| |
| #[arg(long = "max-diff", default_value = "100000", value_parser = clap::builder::RangedI64ValueParser::<usize>::new().range(5000..))] |
| pub max_diff_size: Option<usize>, |
|
|
| |
| |
| |
| |
| |
| #[arg(skip)] |
| pub diff: Option<String>, |
|
|
| |
| |
| |
| |
| |
| pub text: Vec<String>, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct DataCommandGroup { |
| |
| #[arg(long)] |
| pub input: String, |
|
|
| |
| #[arg(long)] |
| pub schema: String, |
|
|
| |
| #[arg(long)] |
| pub system_prompt: Option<String>, |
|
|
| |
| #[arg(long)] |
| pub user_prompt: Option<String>, |
|
|
| |
| #[arg(long, default_value = "10")] |
| pub concurrency: usize, |
| } |
|
|
| impl From<DataCommandGroup> for forge_domain::DataGenerationParameters { |
| fn from(value: DataCommandGroup) -> Self { |
| Self { |
| input: value.input.into(), |
| schema: value.schema.into(), |
| system_prompt: value.system_prompt.map(Into::into), |
| user_prompt: value.user_prompt.map(Into::into), |
| concurrency: value.concurrency, |
| } |
| } |
| } |
|
|
| |
| #[derive(Subcommand, Debug, Clone)] |
| pub enum VscodeCommand { |
| |
| InstallExtension, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct UpdateArgs { |
| |
| #[arg(long, default_value_t = false)] |
| pub no_confirm: bool, |
| } |
|
|
| |
| #[derive(Parser, Debug, Clone)] |
| pub struct LogsArgs { |
| |
| #[arg(long, short = 'n', default_value_t = 20)] |
| pub lines: usize, |
|
|
| |
| #[arg(long)] |
| pub no_follow: bool, |
|
|
| |
| #[arg(long, short = 'l')] |
| pub list: bool, |
|
|
| |
| |
| #[arg(long, short = 'f')] |
| pub file: Option<PathBuf>, |
| } |
|
|
| #[cfg(test)] |
| mod tests { |
| use clap::Parser; |
| use pretty_assertions::assert_eq; |
|
|
| use super::*; |
|
|
| #[test] |
| fn test_data_command_group_conversion() { |
| use std::path::PathBuf; |
|
|
| let fixture = DataCommandGroup { |
| input: "path/to/input.jsonl".to_string(), |
| schema: "path/to/schema.json".to_string(), |
| system_prompt: Some("system prompt".to_string()), |
| user_prompt: None, |
| concurrency: 5, |
| }; |
| let actual: forge_domain::DataGenerationParameters = fixture.into(); |
| let expected = forge_domain::DataGenerationParameters { |
| input: PathBuf::from("path/to/input.jsonl"), |
| schema: PathBuf::from("path/to/schema.json"), |
| system_prompt: Some(PathBuf::from("system prompt")), |
| user_prompt: None, |
| concurrency: 5, |
| }; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_commit_default_max_diff_size() { |
| let fixture = Cli::parse_from(["forge", "commit", "--preview"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Commit(commit)) => commit.max_diff_size, |
| _ => panic!("Expected Commit command"), |
| }; |
| let expected = Some(100000); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_commit_custom_max_diff_size() { |
| let fixture = Cli::parse_from(["forge", "commit", "--preview", "--max-diff", "50000"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Commit(commit)) => commit.max_diff_size, |
| _ => panic!("Expected Commit command"), |
| }; |
| let expected = Some(50000); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_config_set_with_provider_and_model() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "config", |
| "set", |
| "model", |
| "anthropic", |
| "claude-sonnet-4-20250514", |
| ]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Config(config)) => match config.command { |
| ConfigCommand::Set(args) => match args.field { |
| ConfigSetField::Model { provider, model } => { |
| Some((provider.to_string(), model.as_str().to_string())) |
| } |
| _ => None, |
| }, |
| _ => None, |
| }, |
| _ => None, |
| }; |
| let expected = Some(( |
| "Anthropic".to_string(), |
| "claude-sonnet-4-20250514".to_string(), |
| )); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_config_list() { |
| let fixture = Cli::parse_from(["forge", "config", "list"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Config(config)) => matches!(config.command, ConfigCommand::List), |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_config_get_specific_field() { |
| let fixture = Cli::parse_from(["forge", "config", "get", "model"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Config(config)) => match config.command { |
| ConfigCommand::Get(args) => matches!(args.field, ConfigGetField::Model), |
| _ => panic!("Expected ConfigCommand::Get"), |
| }, |
| _ => panic!("Expected TopLevelCommand::Config"), |
| }; |
| assert!(actual); |
| } |
|
|
| #[test] |
| fn test_config_set_commit_with_provider_and_model() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "config", |
| "set", |
| "commit", |
| "anthropic", |
| "claude-haiku-4-20250514", |
| ]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Config(config)) => match config.command { |
| ConfigCommand::Set(args) => match args.field { |
| ConfigSetField::Commit { provider, model } => { |
| Some((provider.to_string(), model.as_str().to_string())) |
| } |
| _ => None, |
| }, |
| _ => None, |
| }, |
| _ => None, |
| }; |
| let expected = Some(( |
| "Anthropic".to_string(), |
| "claude-haiku-4-20250514".to_string(), |
| )); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_conversation_list() { |
| let fixture = Cli::parse_from(["forge", "conversation", "list"]); |
| let is_list = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => { |
| matches!(conversation.command, ConversationCommand::List { .. }) |
| } |
| _ => false, |
| }; |
| assert_eq!(is_list, true); |
| } |
|
|
| #[test] |
| fn test_session_alias_list() { |
| let fixture = Cli::parse_from(["forge", "session", "list"]); |
| let is_list = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => { |
| matches!(conversation.command, ConversationCommand::List { .. }) |
| } |
| _ => false, |
| }; |
| assert_eq!(is_list, true); |
| } |
|
|
| #[test] |
| fn test_agent_id_long_flag() { |
| let fixture = Cli::parse_from(["forge", "--agent", "sage"]); |
| assert_eq!(fixture.agent, Some(AgentId::new("sage"))); |
| } |
|
|
| #[test] |
| fn test_agent_id_short_alias() { |
| let fixture = Cli::parse_from(["forge", "--aid", "muse"]); |
| assert_eq!(fixture.agent, Some(AgentId::new("muse"))); |
| } |
|
|
| #[test] |
| fn test_agent_id_with_prompt() { |
| let fixture = Cli::parse_from(["forge", "--agent", "forge", "-p", "test prompt"]); |
| assert_eq!(fixture.agent, Some(AgentId::new("forge"))); |
| assert_eq!(fixture.prompt, Some("test prompt".to_string())); |
| } |
|
|
| #[test] |
| fn test_agent_id_not_provided() { |
| let fixture = Cli::parse_from(["forge"]); |
| assert_eq!(fixture.agent, None); |
| } |
|
|
| #[test] |
| fn test_conversation_dump_json_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "dump", |
| "550e8400-e29b-41d4-a716-446655440000", |
| ]); |
| let (id, html) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Dump { id, html } => (id, html), |
| _ => (ConversationId::default(), true), |
| }, |
| _ => (ConversationId::default(), true), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440000").unwrap() |
| ); |
| assert_eq!(html, false); |
| } |
|
|
| #[test] |
| fn test_conversation_dump_html_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "dump", |
| "550e8400-e29b-41d4-a716-446655440001", |
| "--html", |
| ]); |
| let (id, html) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Dump { id, html } => (id, html), |
| _ => (ConversationId::default(), false), |
| }, |
| _ => (ConversationId::default(), false), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440001").unwrap() |
| ); |
| assert_eq!(html, true); |
| } |
|
|
| #[test] |
| fn test_conversation_retry_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "retry", |
| "550e8400-e29b-41d4-a716-446655440002", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Retry { id } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440002").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_conversation_compact_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "compact", |
| "550e8400-e29b-41d4-a716-446655440003", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Compact { id } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440003").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_conversation_last_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "show", |
| "550e8400-e29b-41d4-a716-446655440004", |
| ]); |
| let (id, md) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Show { id, md } => (id, md), |
| _ => (ConversationId::default(), false), |
| }, |
| _ => (ConversationId::default(), false), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440004").unwrap() |
| ); |
| assert_eq!(md, false); |
| } |
|
|
| #[test] |
| fn test_conversation_show_with_md_flag() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "show", |
| "550e8400-e29b-41d4-a716-446655440004", |
| "--md", |
| ]); |
| let (id, md) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Show { id, md } => (id, md), |
| _ => (ConversationId::default(), false), |
| }, |
| _ => (ConversationId::default(), false), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440004").unwrap() |
| ); |
| assert_eq!(md, true); |
| } |
|
|
| #[test] |
| fn test_conversation_resume() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "resume", |
| "550e8400-e29b-41d4-a716-446655440005", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Resume { id } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440005").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_list_tools_command_with_agent() { |
| let fixture = Cli::parse_from(["forge", "list", "tool", "sage"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Tool { agent } => agent, |
| _ => AgentId::default(), |
| }, |
| _ => AgentId::default(), |
| }; |
| let expected = AgentId::new("sage"); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_conversation_command() { |
| let fixture = Cli::parse_from(["forge", "list", "conversation"]); |
| let is_conversation_list = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => { |
| matches!(list.command, ListCommand::Conversation { .. }) |
| } |
| _ => false, |
| }; |
| assert_eq!(is_conversation_list, true); |
| } |
|
|
| #[test] |
| fn test_list_session_alias_command() { |
| let fixture = Cli::parse_from(["forge", "list", "session"]); |
| let is_conversation_list = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => { |
| matches!(list.command, ListCommand::Conversation { .. }) |
| } |
| _ => false, |
| }; |
| assert_eq!(is_conversation_list, true); |
| } |
|
|
| #[test] |
| fn test_list_command_without_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "command"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Command { custom } => custom, |
| _ => true, |
| }, |
| _ => true, |
| }; |
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_command_with_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "command", "--custom"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Command { custom } => custom, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_cmd_list_with_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "cmd", "list", "--custom"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Cmd(cmd_group)) => match cmd_group.command { |
| CmdCommand::List { custom } => custom, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_command_list_with_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "command", "list", "--custom"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Cmd(cmd_group)) => match cmd_group.command { |
| CmdCommand::List { custom } => custom, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_info_command_without_porcelain() { |
| let fixture = Cli::parse_from(["forge", "info"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Info { porcelain, .. }) => porcelain, |
| _ => true, |
| }; |
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_info_command_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "info", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Info { porcelain, .. }) => porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_info_command_with_conversation_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "info", |
| "--conversation-id", |
| "550e8400-e29b-41d4-a716-446655440006", |
| ]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Info { conversation_id, .. }) => conversation_id, |
| _ => None, |
| }; |
| let expected = Some(ConversationId::parse("550e8400-e29b-41d4-a716-446655440006").unwrap()); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_info_command_with_cid_alias() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "info", |
| "--cid", |
| "550e8400-e29b-41d4-a716-446655440007", |
| ]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Info { conversation_id, .. }) => conversation_id, |
| _ => None, |
| }; |
| let expected = Some(ConversationId::parse("550e8400-e29b-41d4-a716-446655440007").unwrap()); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_info_command_with_conversation_id_and_porcelain() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "info", |
| "--cid", |
| "550e8400-e29b-41d4-a716-446655440008", |
| "--porcelain", |
| ]); |
| let (conversation_id, porcelain) = match fixture.subcommands { |
| Some(TopLevelCommand::Info { conversation_id, porcelain }) => { |
| (conversation_id, porcelain) |
| } |
| _ => (None, false), |
| }; |
| assert_eq!( |
| conversation_id, |
| Some(ConversationId::parse("550e8400-e29b-41d4-a716-446655440008").unwrap()) |
| ); |
| assert_eq!(porcelain, true); |
| } |
|
|
| #[test] |
| fn test_list_agents_without_porcelain() { |
| let fixture = Cli::parse_from(["forge", "list", "agents"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => list.porcelain, |
| _ => true, |
| }; |
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_agents_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "list", "agents", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => list.porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_mcp_list_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "mcp", "list", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Mcp(mcp)) => mcp.porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_conversation_list_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "conversation", "list", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::List { porcelain } => porcelain, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_models_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "list", "models", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => list.porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_config_list_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "config", "list", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Config(config)) => config.porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_conversation_info_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "info", |
| "550e8400-e29b-41d4-a716-446655440009", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Info { id } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440009").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_conversation_stats_with_porcelain() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "stats", |
| "550e8400-e29b-41d4-a716-446655440010", |
| "--porcelain", |
| ]); |
| let (id, porcelain) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Stats { id, porcelain } => (id, porcelain), |
| _ => (ConversationId::default(), false), |
| }, |
| _ => (ConversationId::default(), false), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440010").unwrap() |
| ); |
| assert_eq!(porcelain, true); |
| } |
|
|
| #[test] |
| fn test_prompt_command() { |
| let fixture = Cli::parse_from(["forge", "zsh", "rprompt"]); |
| let r_prompt = matches!( |
| fixture.subcommands, |
| Some(TopLevelCommand::Zsh(ZshCommandGroup::Rprompt)) |
| ); |
| assert!(r_prompt); |
| } |
|
|
| #[test] |
| fn test_session_alias_dump() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "session", |
| "dump", |
| "550e8400-e29b-41d4-a716-446655440011", |
| ]); |
| let (id, html) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Dump { id, html } => (id, html), |
| _ => (ConversationId::default(), true), |
| }, |
| _ => (ConversationId::default(), true), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440011").unwrap() |
| ); |
| assert_eq!(html, false); |
| } |
|
|
| #[test] |
| fn test_session_alias_retry() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "session", |
| "retry", |
| "550e8400-e29b-41d4-a716-446655440012", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Retry { id } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440012").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_prompt_with_conversation_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "-p", |
| "hello", |
| "--conversation-id", |
| "550e8400-e29b-41d4-a716-446655440000", |
| ]); |
| let actual = fixture.conversation_id; |
| let expected = Some(ConversationId::parse("550e8400-e29b-41d4-a716-446655440000").unwrap()); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_conversation_id_without_prompt() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "--conversation-id", |
| "550e8400-e29b-41d4-a716-446655440000", |
| ]); |
| let actual = fixture.conversation_id; |
| let expected = Some(ConversationId::parse("550e8400-e29b-41d4-a716-446655440000").unwrap()); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_conversation_clone_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "clone", |
| "550e8400-e29b-41d4-a716-446655440013", |
| ]); |
| let id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Clone { id, .. } => id, |
| _ => ConversationId::default(), |
| }, |
| _ => ConversationId::default(), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440013").unwrap() |
| ); |
| } |
|
|
| #[test] |
| fn test_conversation_clone_with_porcelain() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "clone", |
| "550e8400-e29b-41d4-a716-446655440014", |
| "--porcelain", |
| ]); |
| let (id, porcelain) = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => match conversation.command { |
| ConversationCommand::Clone { id, porcelain } => (id, porcelain), |
| _ => (ConversationId::default(), false), |
| }, |
| _ => (ConversationId::default(), false), |
| }; |
| assert_eq!( |
| id, |
| ConversationId::parse("550e8400-e29b-41d4-a716-446655440014").unwrap() |
| ); |
| assert_eq!(porcelain, true); |
| } |
|
|
| #[test] |
| fn test_cmd_command_with_args() { |
| let fixture = |
| Cli::parse_from(["forge", "cmd", "execute", "custom-command", "arg1", "arg2"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Cmd(run_group)) => match run_group.command { |
| CmdCommand::Execute { commands } => commands.join(" "), |
| _ => panic!("Expected Execute command"), |
| }, |
| _ => panic!("Expected Cmd command"), |
| }; |
| let expected = "custom-command arg1 arg2".to_string(); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_is_interactive_without_flags() { |
| let fixture = Cli::parse_from(["forge"]); |
| let actual = fixture.is_interactive(); |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_commit_with_custom_text() { |
| let fixture = Cli::parse_from(["forge", "commit", "fix", "typo", "in", "readme"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Commit(commit)) => commit.text, |
| _ => panic!("Expected Commit command"), |
| }; |
| let expected = ["fix", "typo", "in", "readme"] |
| .iter() |
| .map(|s| s.to_string()) |
| .collect::<Vec<String>>(); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_commit_without_custom_text() { |
| let fixture = Cli::parse_from(["forge", "commit", "--preview"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Commit(commit)) => commit.text, |
| _ => panic!("Expected Commit command"), |
| }; |
| let expected: Vec<String> = vec![]; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_commit_with_text_and_flags() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "commit", |
| "--preview", |
| "--max-diff", |
| "50000", |
| "update", |
| "docs", |
| ]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Commit(commit)) => { |
| (commit.preview, commit.max_diff_size, commit.text) |
| } |
| _ => panic!("Expected Commit command"), |
| }; |
| let expected = ( |
| true, |
| Some(50000), |
| ["update", "docs"] |
| .iter() |
| .map(|s| s.to_string()) |
| .collect::<Vec<String>>(), |
| ); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_skill_command() { |
| let fixture = Cli::parse_from(["forge", "list", "skill"]); |
| let is_skill_list = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => matches!(list.command, ListCommand::Skill { .. }), |
| _ => false, |
| }; |
| assert_eq!(is_skill_list, true); |
| } |
|
|
| #[test] |
| fn test_list_skills_alias_command() { |
| let fixture = Cli::parse_from(["forge", "list", "skills"]); |
| let is_skill_list = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => matches!(list.command, ListCommand::Skill { .. }), |
| _ => false, |
| }; |
| assert_eq!(is_skill_list, true); |
| } |
|
|
| #[test] |
| fn test_conversation_delete_with_id() { |
| let fixture = Cli::parse_from([ |
| "forge", |
| "conversation", |
| "delete", |
| "550e8400-e29b-41d4-a716-446655440000", |
| ]); |
| let is_delete_with_id = match fixture.subcommands { |
| Some(TopLevelCommand::Conversation(conversation)) => { |
| matches!(conversation.command, ConversationCommand::Delete { id: _ }) |
| } |
| _ => false, |
| }; |
| assert_eq!(is_delete_with_id, true); |
| } |
|
|
| #[test] |
| fn test_list_skill_with_porcelain() { |
| let fixture = Cli::parse_from(["forge", "list", "skill", "--porcelain"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => list.porcelain, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_prompt_with_leading_hyphen() { |
| let fixture = Cli::parse_from(["forge", "-p", "- hi"]); |
| assert_eq!(fixture.prompt, Some("- hi".to_string())); |
| } |
|
|
| #[test] |
| fn test_prompt_with_hyphen_flag_like_value() { |
| let fixture = Cli::parse_from(["forge", "-p", "-test"]); |
| assert_eq!(fixture.prompt, Some("-test".to_string())); |
| } |
|
|
| #[test] |
| fn test_prompt_with_double_hyphen() { |
| let fixture = Cli::parse_from(["forge", "-p", "--something"]); |
| assert_eq!(fixture.prompt, Some("--something".to_string())); |
| } |
|
|
| #[test] |
| fn test_suggest_with_dash_prefixed_prompt() { |
| let fixture = Cli::parse_from(["forge", "suggest", "--- date"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Suggest { prompt }) => prompt, |
| _ => panic!("Expected suggest subcommand"), |
| }; |
| let expected = "--- date".to_string(); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_suggest_with_double_dash_prompt() { |
| let fixture = Cli::parse_from(["forge", "suggest", "--date tomorrow"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Suggest { prompt }) => prompt, |
| _ => panic!("Expected suggest subcommand"), |
| }; |
| let expected = "--date tomorrow".to_string(); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_suggest_with_single_dash_prompt() { |
| let fixture = Cli::parse_from(["forge", "suggest", "-v file.txt"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Suggest { prompt }) => prompt, |
| _ => panic!("Expected suggest subcommand"), |
| }; |
| let expected = "-v file.txt".to_string(); |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_terminal_theme_zsh() { |
| let fixture = Cli::parse_from(["forge", "zsh", "theme"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(terminal)) => { |
| matches!(terminal, ZshCommandGroup::Theme) |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_terminal_plugin_zsh() { |
| let fixture = Cli::parse_from(["forge", "zsh", "plugin"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(terminal)) => { |
| matches!(terminal, ZshCommandGroup::Plugin) |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_zsh_doctor() { |
| let fixture = Cli::parse_from(["forge", "zsh", "doctor"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(terminal)) => { |
| matches!(terminal, ZshCommandGroup::Doctor) |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_zsh_setup() { |
| let fixture = Cli::parse_from(["forge", "zsh", "setup"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(terminal)) => { |
| matches!(terminal, ZshCommandGroup::Setup) |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_zsh_keyboard() { |
| let fixture = Cli::parse_from(["forge", "zsh", "keyboard"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(terminal)) => { |
| matches!(terminal, ZshCommandGroup::Keyboard) |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_zsh_format() { |
| let fixture = Cli::parse_from(["forge", "zsh", "format", "--buffer", "hello world"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Zsh(ZshCommandGroup::Format { buffer })) => { |
| buffer == "hello world" |
| } |
| _ => false, |
| }; |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_setup_alias() { |
| let fixture = Cli::parse_from(["forge", "setup"]); |
| let actual = matches!(fixture.subcommands, Some(TopLevelCommand::Setup)); |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_doctor_alias() { |
| let fixture = Cli::parse_from(["forge", "doctor"]); |
| let actual = matches!(fixture.subcommands, Some(TopLevelCommand::Doctor)); |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_install_vscode_extension() { |
| let fixture = Cli::parse_from(["forge", "vscode", "install-extension"]); |
| let actual = matches!( |
| fixture.subcommands, |
| Some(TopLevelCommand::Vscode(VscodeCommand::InstallExtension)) |
| ); |
| assert_eq!(actual, true); |
| } |
|
|
| #[test] |
| fn test_list_agent_with_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "agent", "--custom"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Agent { custom } => custom, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_agent_without_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "agent"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Agent { custom } => custom, |
| _ => true, |
| }, |
| _ => true, |
| }; |
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_skill_with_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "skill", "--custom"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Skill { custom } => custom, |
| _ => false, |
| }, |
| _ => false, |
| }; |
| let expected = true; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_list_skill_without_custom_flag() { |
| let fixture = Cli::parse_from(["forge", "list", "skill"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::List(list)) => match list.command { |
| ListCommand::Skill { custom } => custom, |
| _ => true, |
| }, |
| _ => true, |
| }; |
| let expected = false; |
| assert_eq!(actual, expected); |
| } |
|
|
| #[test] |
| fn test_update_with_no_confirm() { |
| let fixture = Cli::parse_from(["forge", "update", "--no-confirm"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Update(args)) => args.no_confirm, |
| _ => panic!("Expected Update command"), |
| }; |
| assert!(actual); |
| } |
|
|
| #[test] |
| fn test_update_without_no_confirm() { |
| let fixture = Cli::parse_from(["forge", "update"]); |
| let actual = match fixture.subcommands { |
| Some(TopLevelCommand::Update(args)) => args.no_confirm, |
| _ => panic!("Expected Update command"), |
| }; |
| assert!(!actual); |
| } |
| } |
|
|