use std::path::PathBuf;
use anyhow::{anyhow, Result};
use clap::{Args, Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "share-the-mark",
version,
about = "share-the-mark: receive design-feedback change-briefs and expose them to a coding agent"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
Serve(ServeArgs),
Start(ServeArgs),
Stop {
#[arg(long)]
port: Option<u16>,
},
Status {
#[arg(long)]
port: Option<u16>,
},
Pending {
#[arg(long)]
dir: Option<PathBuf>,
},
List {
#[arg(long)]
all: bool,
#[arg(long)]
dir: Option<PathBuf>,
},
Show {
id: String,
#[arg(long)]
json: bool,
#[arg(long)]
keep_unread: bool,
#[arg(long)]
dir: Option<PathBuf>,
},
Request {
target: String,
#[arg(long)]
bundle: Option<PathBuf>,
#[arg(long)]
playwright: bool,
#[arg(long)]
json: bool,
#[arg(long, default_value_t = 600)]
timeout: u64,
#[arg(long)]
port: Option<u16>,
#[arg(long)]
dir: Option<PathBuf>,
},
Skill {
#[command(subcommand)]
command: SkillCommand,
},
Setup {
#[arg(long)]
no_browser: bool,
},
}
#[derive(Args)]
pub struct ServeArgs {
#[arg(long)]
pub port: Option<u16>,
#[arg(long)]
pub dir: Option<PathBuf>,
#[arg(long)]
pub idle_timeout: Option<u64>,
}
#[derive(Subcommand)]
pub enum SkillCommand {
Install {
#[arg(long)]
dir: Option<PathBuf>,
},
}
pub fn resolve_port(flag: Option<u16>) -> u16 {
flag.or_else(|| {
std::env::var("SHARE_THE_MARK_PORT")
.ok()
.and_then(|s| s.parse().ok())
})
.unwrap_or(8787)
}
pub const BACKGROUND_IDLE_SECS: u64 = 3 * 60 * 60;
fn idle_or(flag: Option<u64>, default: u64) -> u64 {
flag.or_else(|| {
std::env::var("SHARE_THE_MARK_IDLE")
.ok()
.and_then(|s| s.parse().ok())
})
.unwrap_or(default)
}
pub fn resolve_idle(flag: Option<u64>) -> u64 {
idle_or(flag, 0)
}
pub fn resolve_background_idle(flag: Option<u64>) -> u64 {
idle_or(flag, BACKGROUND_IDLE_SECS)
}
pub fn resolve_dir(flag: Option<PathBuf>) -> Result<PathBuf> {
if let Some(dir) = flag {
return Ok(dir);
}
if let Ok(dir) = std::env::var("SHARE_THE_MARK_DIR") {
return Ok(PathBuf::from(dir));
}
let project = directories::ProjectDirs::from("", "", "share-the-mark")
.ok_or_else(|| anyhow!("cannot determine a data directory"))?;
Ok(project.data_dir().to_path_buf())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn explicit_idle_flag_overrides_both_defaults() {
assert_eq!(resolve_idle(Some(42)), 42);
assert_eq!(resolve_background_idle(Some(42)), 42);
assert_eq!(resolve_background_idle(Some(0)), 0);
}
#[test]
fn background_idle_default_is_a_generous_three_hours() {
assert_eq!(BACKGROUND_IDLE_SECS, 3 * 60 * 60);
}
}