use clap::Parser;
mod cmd;
mod descmd;
mod audit;
mod errors;
mod lint;
mod output;
mod scope;
mod search;
mod since;
mod stdin_input;
mod store_open;
#[derive(Parser)]
#[command(
name = "cliban",
version,
about = "AI-agent-first kanban board for the terminal"
)]
struct Cli {
#[arg(long, global = true)]
db: Option<String>,
#[command(subcommand)]
cmd: Option<Command>,
}
#[derive(clap::Subcommand)]
enum Command {
Tui,
Project(cmd::project::ProjectArgs),
Label(cmd::label::LabelArgs),
Issue(Box<cmd::issue::IssueArgs>),
Activity(cmd::activity::ActivityArgs),
Milestone(cmd::milestone::MilestoneArgs),
#[cfg(feature = "linear")]
Linear(cmd::sync::LinearArgs),
#[command(hide = true)]
Ls(cmd::issue::LsArgs),
#[command(hide = true)]
Mv(cmd::issue::MvArgs),
#[command(hide = true)]
Rm(cmd::issue::RmArgs),
#[command(hide = true)]
Show(cmd::issue::ShowArgs),
#[command(hide = true)]
Log(cmd::issue::LogArgs),
#[command(hide = true)]
Tick(cmd::issue::TickArgs),
#[command(hide = true)]
Cat(cmd::issue::CatArgs),
}
fn issue_default(cmd: cmd::issue::IssueCmd) -> cmd::issue::IssueArgs {
cmd::issue::IssueArgs { cmd }
}
#[cfg(unix)]
fn reset_sigpipe() {
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
}
#[cfg(not(unix))]
fn reset_sigpipe() {}
fn main() {
reset_sigpipe();
let cli = Cli::parse();
{
use std::io::IsTerminal;
if cli.cmd.is_none() && !std::io::stdout().is_terminal() {
use clap::CommandFactory;
let _ = Cli::command().print_help();
std::process::exit(2);
}
}
if matches!(cli.cmd, None | Some(Command::Tui)) {
let path = store_open::db_path(&cli.db);
if let Err(e) = cliban_tui::run(path) {
eprintln!("error: {e}");
std::process::exit(1);
}
return;
}
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("build tokio runtime");
if let Err(e) = rt.block_on(run(cli)) {
eprintln!("error: {}", e.message());
std::process::exit(e.code());
}
}
async fn run(cli: Cli) -> errors::CliResult<()> {
match cli.cmd {
None | Some(Command::Tui) => unreachable!("TUI handled in main before runtime"),
Some(Command::Project(args)) => cmd::project::run(&cli.db, args).await,
Some(Command::Label(args)) => cmd::label::run(&cli.db, args).await,
Some(Command::Issue(args)) => cmd::issue::run(&cli.db, *args).await,
Some(Command::Activity(args)) => cmd::activity::run(&cli.db, args).await,
Some(Command::Milestone(args)) => cmd::milestone::run(&cli.db, args).await,
#[cfg(feature = "linear")]
Some(Command::Linear(args)) => cmd::sync::run_linear(&cli.db, args).await,
Some(Command::Ls(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Ls(a))).await
}
Some(Command::Mv(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Mv(a))).await
}
Some(Command::Rm(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Rm(a))).await
}
Some(Command::Show(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Show(a))).await
}
Some(Command::Log(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Log(a))).await
}
Some(Command::Tick(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Tick(a))).await
}
Some(Command::Cat(a)) => {
cmd::issue::run(&cli.db, issue_default(cmd::issue::IssueCmd::Cat(a))).await
}
}
}