use clap::{Args, Parser, Subcommand};
#[derive(Parser)]
#[command(name = "myc")]
#[command(about = "A robust, production-grade task/plan manager CLI")]
#[command(version = env!("CARGO_PKG_VERSION"))]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(global = true, short, long, default_value = "table")]
pub format: OutputFormat,
#[arg(global = true, short, long)]
pub quiet: bool,
}
#[derive(Subcommand)]
pub enum Commands {
Init,
PrimeAgents {
#[arg(long)]
force: bool,
#[arg(long)]
path: Option<std::path::PathBuf>,
},
#[command(subcommand)]
Epic(EpicCommands),
#[command(subcommand)]
Task(TaskCommands),
#[command(subcommand)]
Assignee(AssigneeCommands),
#[command(subcommand)]
Deps(DepsCommands),
List(ListArgs),
Summary,
#[command(subcommand)]
Export(ExportCommands),
Doctor {
#[arg(long)]
fix: bool,
},
#[command(subcommand)]
Linear(LinearCommands),
#[command(subcommand, alias = "fu")]
Followup(FollowupCommands),
}
#[derive(Subcommand)]
pub enum FollowupCommands {
Add {
body: String,
#[arg(short, long)]
title: Option<String>,
},
List {
#[arg(short, long, conflicts_with_all = ["all", "open", "closed"])]
status: Option<String>,
#[arg(short = 'a', long, conflicts_with_all = ["open", "closed"])]
all: bool,
#[arg(short = 'o', long, conflicts_with = "closed")]
open: bool,
#[arg(short = 'c', long)]
closed: bool,
},
Show {
id: i64,
},
Next,
Start {
id: i64,
},
Done {
id: i64,
#[arg(short, long)]
reason: Option<String>,
},
Wontfix {
id: i64,
#[arg(short, long)]
reason: Option<String>,
},
Reopen {
id: i64,
},
Edit {
id: i64,
#[arg(short, long)]
body: Option<String>,
#[arg(short, long)]
title: Option<String>,
},
Append {
id: i64,
text: String,
},
Rm {
id: i64,
#[arg(long)]
force: bool,
},
Promote {
id: i64,
#[arg(short, long)]
epic: Option<i64>,
#[arg(short, long, default_value = "medium")]
priority: String,
},
Count,
}
#[derive(Subcommand)]
pub enum LinearCommands {
Setup,
Sync {
#[arg(long)]
force_local: bool,
#[arg(long)]
force_remote: bool,
},
Push,
Pull,
Status,
Unlink,
}
#[derive(Subcommand)]
pub enum EpicCommands {
Create {
#[arg(short, long)]
title: String,
#[arg(short, long)]
description: Option<String>,
#[arg(short = 'n', long)]
notes: Option<String>,
#[arg(long)]
user_info: Option<String>,
},
List,
Show {
id: i64,
},
Update {
id: i64,
#[arg(short, long)]
title: Option<String>,
#[arg(short, long)]
description: Option<String>,
#[arg(short, long, value_parser = ["open", "in_progress", "closed"])]
status: Option<String>,
#[arg(short = 'n', long)]
notes: Option<String>,
#[arg(long)]
user_info: Option<String>,
#[arg(long)]
agent_questions: Option<String>,
},
Note {
epic_id: i64,
content: String,
},
Notes {
epic_id: i64,
},
Delete {
id: i64,
#[arg(long)]
force: bool,
},
}
#[derive(Subcommand)]
pub enum TaskCommands {
Create {
#[arg(short, long)]
title: String,
#[arg(short, long)]
description: Option<String>,
#[arg(short, long)]
epic: Option<i64>,
#[arg(short, long, default_value = "medium")]
priority: String,
#[arg(short, long)]
assignee: Option<i64>,
#[arg(short = 'u', long)]
due: Option<String>,
#[arg(short = 'g', long)]
tags: Option<String>,
#[arg(short = 'n', long)]
notes: Option<String>,
#[arg(long)]
user_info: Option<String>,
#[arg(short = 'm', long)]
template: Option<String>,
},
List {
#[arg(short, long)]
epic: Option<i64>,
#[arg(short, long)]
status: Option<String>,
#[arg(short, long)]
priority: Option<String>,
#[arg(short, long)]
assignee: Option<i64>,
#[arg(long)]
blocked: bool,
#[arg(long)]
overdue: bool,
#[arg(short, long)]
tag: Option<String>,
#[arg(long)]
all: bool,
},
Batch {
#[arg(short, long)]
file: String,
},
Show {
id: i64,
},
Update {
id: i64,
#[arg(short, long)]
title: Option<String>,
#[arg(short, long)]
description: Option<String>,
#[arg(short, long)]
status: Option<String>,
#[arg(short, long)]
priority: Option<String>,
#[arg(short, long)]
epic: Option<i64>,
#[arg(short, long)]
assignee: Option<i64>,
#[arg(short = 'u', long)]
due: Option<String>,
#[arg(short = 'g', long)]
tags: Option<String>,
#[arg(short = 'n', long)]
notes: Option<String>,
#[arg(long)]
user_info: Option<String>,
#[arg(long)]
agent_questions: Option<String>,
},
Delete {
id: i64,
#[arg(long)]
force: bool,
},
Assign {
task_id: i64,
assignee_id: i64,
},
#[command(subcommand)]
Link(LinkCommands),
Unlink {
ref_id: i64,
},
Close {
id: i64,
#[arg(long)]
force: bool,
},
Reopen {
id: i64,
},
Note {
task_id: i64,
content: String,
},
Notes {
task_id: i64,
},
Clone {
id: i64,
#[arg(short, long)]
title: Option<String>,
},
#[command(subcommand)]
BatchOp(BatchOpCommands),
}
#[derive(Subcommand)]
pub enum BatchOpCommands {
Close {
ids: Vec<i64>,
#[arg(long)]
force: bool,
},
Tag {
tag: String,
ids: Vec<i64>,
},
Move {
epic_id: i64,
ids: Vec<i64>,
},
DeleteOrphans {
#[arg(long)]
force: bool,
},
}
#[derive(Subcommand)]
pub enum LinkCommands {
GithubIssue {
#[arg(short, long)]
task: i64,
reference: String,
},
GithubPr {
#[arg(short, long)]
task: i64,
reference: String,
},
Url {
#[arg(short, long)]
task: i64,
url: String,
},
Blocks {
#[arg(short, long)]
task: i64,
blocked: i64,
},
}
#[derive(Subcommand)]
pub enum AssigneeCommands {
Create {
#[arg(short, long)]
name: String,
#[arg(short, long)]
email: Option<String>,
#[arg(short, long)]
github: Option<String>,
},
List,
Show {
id: i64,
},
Delete {
id: i64,
#[arg(short, long)]
force: bool,
},
}
#[derive(Subcommand)]
pub enum DepsCommands {
Show {
task_id: i64,
},
Unlink {
task_id: i64,
blocked_task_id: i64,
},
}
#[derive(Args)]
pub struct ListArgs {
#[arg(short, long)]
pub epic: Option<i64>,
#[arg(short, long)]
pub status: Option<String>,
#[arg(short, long)]
pub priority: Option<String>,
#[arg(short, long)]
pub assignee: Option<i64>,
#[arg(long)]
pub blocked: bool,
#[arg(long)]
pub overdue: bool,
#[arg(short, long)]
pub tag: Option<String>,
#[arg(long)]
pub all: bool,
}
#[derive(Subcommand)]
pub enum ExportCommands {
Json {
#[arg(short, long)]
output: Option<String>,
},
Csv {
#[arg(short, long)]
output: Option<String>,
},
}
#[derive(Clone, Debug)]
pub enum OutputFormat {
Table,
Json,
}
impl std::str::FromStr for OutputFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"table" => Ok(OutputFormat::Table),
"json" => Ok(OutputFormat::Json),
_ => Err(format!("Invalid format: {}. Use 'table' or 'json'", s)),
}
}
}
impl std::fmt::Display for OutputFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OutputFormat::Table => write!(f, "table"),
OutputFormat::Json => write!(f, "json"),
}
}
}