@@ -16,14 +16,209 @@
/// Repository (org/repo)
repo: Option<String>,
},
/// Initialize the default board columns (Backlog/In Progress/Done)
Init {
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
},
/// Create a board column
#[command(name = "create-column")]
CreateColumn {
/// Column name
name: String,
/// Position (0-based)
#[arg(long)]
position: Option<u32>,
/// WIP limit
#[arg(long)]
wip_limit: Option<u32>,
/// Closing issues moves them here
#[arg(long)]
closed_column: bool,
/// Column color (hex)
#[arg(long)]
color: Option<String>,
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
},
/// Edit a board column
#[command(name = "edit-column")]
EditColumn {
/// Column short ID or UUID
id: String,
/// New name
#[arg(long)]
name: Option<String>,
/// New position
#[arg(long)]
position: Option<u32>,
/// New WIP limit
#[arg(long)]
wip_limit: Option<u32>,
/// New color (hex)
#[arg(long)]
color: Option<String>,
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
},
/// Delete a board column (must be empty)
#[command(name = "delete-column")]
DeleteColumn {
/// Column short ID or UUID
id: String,
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
},
}
pub async fn run(args: BoardArgs) -> Result<(), Box<dyn std::error::Error>> {
match args.command {
BoardCommand::List { repo } => list(repo.as_deref()).await,
BoardCommand::Init { repo } => init(repo.as_deref()).await,
BoardCommand::CreateColumn {
name,
position,
wip_limit,
closed_column,
color,
repo,
} => {
create_column(
repo.as_deref(),
&name,
position,
wip_limit,
closed_column,
color.as_deref(),
)
.await
}
BoardCommand::EditColumn {
id,
name,
position,
wip_limit,
color,
repo,
} => {
edit_column(
repo.as_deref(),
&id,
name.as_deref(),
position,
wip_limit,
color.as_deref(),
)
.await
}
BoardCommand::DeleteColumn { id, repo } => delete_column(repo.as_deref(), &id).await,
}
}
async fn init(repo: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let resp: serde_json::Value = client
.post(
&format!("/{org}/{name}/board/initialize"),
&serde_json::json!({}),
)
.await?;
if output::is_json() {
output::json_ok("columns", resp["columns"].clone());
return Ok(());
}
let count = resp["columns"].as_array().map(|a| a.len()).unwrap_or(0);
output::success(&format!("Initialized board with {count} columns"));
Ok(())
}
async fn create_column(
repo: Option<&str>,
col_name: &str,
position: Option<u32>,
wip_limit: Option<u32>,
closed_column: bool,
color: Option<&str>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let mut payload = serde_json::json!({"name": col_name});
if let Some(v) = position {
payload["position"] = serde_json::json!(v);
}
if let Some(v) = wip_limit {
payload["wip_limit"] = serde_json::json!(v);
}
if closed_column {
payload["is_closed_column"] = serde_json::json!(true);
}
if let Some(v) = color {
payload["color"] = serde_json::json!(v);
}
let resp: serde_json::Value = client
.post(&format!("/{org}/{name}/board/columns"), &payload)
.await?;
if output::is_json() {
output::json_ok("column", resp["column"].clone());
return Ok(());
}
output::success(&format!("Created column '{col_name}'"));
Ok(())
}
async fn edit_column(
repo: Option<&str>,
id: &str,
new_name: Option<&str>,
position: Option<u32>,
wip_limit: Option<u32>,
color: Option<&str>,
) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let mut payload = serde_json::json!({});
if let Some(v) = new_name {
payload["name"] = serde_json::json!(v);
}
if let Some(v) = position {
payload["position"] = serde_json::json!(v);
}
if let Some(v) = wip_limit {
payload["wip_limit"] = serde_json::json!(v);
}
if let Some(v) = color {
payload["color"] = serde_json::json!(v);
}
let resp: serde_json::Value = client
.patch(&format!("/{org}/{name}/board/columns/{id}"), &payload)
.await?;
if output::is_json() {
output::json_ok("column", resp["column"].clone());
return Ok(());
}
output::success(&format!("Updated column '{id}'"));
Ok(())
}
async fn delete_column(repo: Option<&str>, id: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
client
.delete_empty(&format!("/{org}/{name}/board/columns/{id}"))
.await?;
if output::is_json() {
output::json_ok("deleted", serde_json::json!(id));
return Ok(());
}
output::success(&format!("Deleted column '{id}'"));
Ok(())
}
async fn list(repo: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
@@ -39,6 +234,11 @@
return Err(e.into());
}
};
if output::is_json() {
output::print_json(&resp);
return Ok(());
}
// Parse and display columns
let columns = resp