1use anyhow::Result;
2use clap::{Parser, Subcommand};
3
4use crate::duration::parse_idle;
5use crate::paths::Context;
6use crate::{attach, doctor, list, notify, resume, rm, server, service, setup, tui};
7
8#[derive(Debug, Parser)]
10#[command(name = "agent-berth", version, about, propagate_version = true)]
11struct Cli {
12 #[command(subcommand)]
13 command: Option<Command>,
14}
15
16#[derive(Debug, Subcommand)]
17enum Command {
18 Tui,
20 Server,
22 Setup {
24 #[arg(long)]
26 no_service: bool,
27 },
28 Teardown,
30 Service {
32 #[command(subcommand)]
33 action: ServiceAction,
34 },
35 List {
37 #[arg(long)]
39 json: bool,
40 #[arg(long)]
42 resumable: bool,
43 #[arg(
45 long,
46 value_name = "DURATION",
47 requires = "resumable",
48 num_args = 0..=1,
49 default_missing_value = "20m"
50 )]
51 idle: Option<String>,
52 #[arg(long, requires = "resumable")]
54 here: bool,
55 },
56 Notify {
58 #[arg(long)]
59 provider: String,
60 },
61 Attach {
63 query: Option<String>,
65 #[arg(short, long)]
67 preview: bool,
68 #[arg(short, long)]
70 session: bool,
71 #[arg(long)]
73 dry_run: bool,
74 },
75 Doctor,
77 Resume {
79 pattern: Option<String>,
81 #[arg(
83 long,
84 value_name = "DURATION",
85 num_args = 0..=1,
86 default_missing_value = "20m"
87 )]
88 idle: Option<String>,
89 #[arg(long)]
91 here: bool,
92 #[arg(long)]
94 dry_run: bool,
95 },
96 #[command(alias = "remove")]
98 Rm {
99 patterns: Vec<String>,
101 },
102}
103
104#[derive(Debug, Subcommand)]
105enum ServiceAction {
106 Start,
108 Stop,
110 Restart,
112}
113
114pub fn run() -> Result<()> {
115 let cli = Cli::parse();
116 let ctx = Context::from_env()?;
117 let Some(command) = cli.command else {
118 if !std::io::IsTerminal::is_terminal(&std::io::stdout()) {
119 return list::run(&ctx, false, false, None, false);
120 }
121 return tui::run(&ctx);
122 };
123 match command {
124 Command::Tui => {
125 if !std::io::IsTerminal::is_terminal(&std::io::stdout()) {
126 anyhow::bail!("tui requires a terminal");
127 }
128 tui::run(&ctx)
129 }
130 Command::Server => server::run(&ctx),
131 Command::Setup { no_service } => setup::setup(&ctx, no_service),
132 Command::Teardown => setup::teardown(&ctx),
133 Command::Service { action } => match action {
134 ServiceAction::Start => service::start(&ctx),
135 ServiceAction::Stop => service::stop(&ctx),
136 ServiceAction::Restart => service::restart(&ctx),
137 },
138 Command::List {
139 json,
140 resumable,
141 idle,
142 here,
143 } => {
144 let idle = if resumable {
145 parse_idle(idle.as_deref())?
146 } else {
147 None
148 };
149 list::run(&ctx, json, resumable, idle, here)
150 }
151 Command::Notify { provider } => notify::run(&ctx, provider),
152 Command::Attach {
153 query,
154 preview,
155 session,
156 dry_run,
157 } => attach::run(&ctx, query, preview, session, dry_run),
158 Command::Doctor => doctor::run(&ctx),
159 Command::Resume {
160 pattern,
161 idle,
162 here,
163 dry_run,
164 } => resume::run(&ctx, parse_idle(idle.as_deref())?, pattern, here, dry_run),
165 Command::Rm { patterns } => rm::run(&ctx, patterns),
166 }
167}