diff --git a/src/cli/commands/list.rs b/src/cli/commands/list.rs index 2946008..e540546 100644 --- a/src/cli/commands/list.rs +++ b/src/cli/commands/list.rs @@ -59,7 +59,7 @@ pub struct IssueListRow { pub unresolved_count: i64, #[serde(skip_serializing_if = "Option::is_none")] pub status_name: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing)] pub status_category: Option, #[serde(skip_serializing_if = "Option::is_none")] pub status_color: Option, @@ -86,7 +86,7 @@ pub struct IssueListRowJson { pub project_path: String, #[serde(skip_serializing_if = "Option::is_none")] pub status_name: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing)] pub status_category: Option, #[serde(skip_serializing_if = "Option::is_none")] pub status_color: Option, @@ -124,6 +124,7 @@ impl From<&IssueListRow> for IssueListRowJson { pub struct ListResult { pub issues: Vec, pub total_count: usize, + pub available_statuses: Vec, } #[derive(Serialize)] @@ -268,10 +269,21 @@ pub fn run_list_issues(config: &Config, filters: ListFilters) -> Result Result> { + let mut stmt = conn.prepare( + "SELECT DISTINCT status_name FROM issues WHERE status_name IS NOT NULL ORDER BY status_name", + )?; + let statuses = stmt + .query_map([], |row| row.get::<_, String>(0))? + .collect::, _>>()?; + Ok(statuses) +} + fn query_issues(conn: &Connection, filters: &ListFilters) -> Result { let mut where_clauses = Vec::new(); let mut params: Vec> = Vec::new(); @@ -457,6 +469,7 @@ fn query_issues(conn: &Connection, filters: &ListFilters) -> Result Ok(ListResult { issues, total_count, + available_statuses: Vec::new(), }) } @@ -822,11 +835,13 @@ pub fn print_list_issues(result: &ListResult) { pub fn print_list_issues_json(result: &ListResult, elapsed_ms: u64, fields: Option<&[String]>) { let json_result = ListResultJson::from(result); - let meta = RobotMeta { elapsed_ms }; let output = serde_json::json!({ "ok": true, "data": json_result, - "meta": meta, + "meta": { + "elapsed_ms": elapsed_ms, + "available_statuses": result.available_statuses, + }, }); let mut output = output; if let Some(f) = fields { diff --git a/src/cli/commands/show.rs b/src/cli/commands/show.rs index 7087bc9..1d08b09 100644 --- a/src/cli/commands/show.rs +++ b/src/cli/commands/show.rs @@ -628,13 +628,9 @@ pub fn print_show_issue(issue: &IssueDetail) { println!("State: {}", state_styled); if let Some(status) = &issue.status_name { - let display = match &issue.status_category { - Some(cat) => format!("{status} ({})", cat.to_ascii_lowercase()), - None => status.clone(), - }; println!( "Status: {}", - style_with_hex(&display, issue.status_color.as_deref()) + style_with_hex(status, issue.status_color.as_deref()) ); } @@ -944,6 +940,7 @@ pub struct IssueDetailJson { pub closing_merge_requests: Vec, pub discussions: Vec, pub status_name: Option, + #[serde(skip_serializing)] pub status_category: Option, pub status_color: Option, pub status_icon_name: Option, diff --git a/src/main.rs b/src/main.rs index b518793..262c44c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2109,11 +2109,15 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box", "-n/--limit", "--fields ", "-s/--state", "--status ", "-p/--project", "-a/--author", "-A/--assignee", "-l/--label", "-m/--milestone", "--since", "--due-before", "--has-due", "--no-has-due", "--sort", "--asc", "--no-asc", "-o/--open", "--no-open"], "example": "lore --robot issues --state opened --limit 10", + "notes": { + "status_filter": "--status filters by work item status NAME (case-insensitive). Valid values are in meta.available_statuses of any issues list response.", + "status_name": "status_name is the board column label (e.g. 'In review', 'Blocked'). This is the canonical status identifier for filtering." + }, "response_schema": { "list": { "ok": "bool", - "data": {"issues": "[{iid:int, title:string, state:string, author_username:string, labels:[string], assignees:[string], discussion_count:int, unresolved_count:int, created_at_iso:string, updated_at_iso:string, web_url:string?, project_path:string}]", "total_count": "int", "showing": "int"}, - "meta": {"elapsed_ms": "int"} + "data": {"issues": "[{iid:int, title:string, state:string, author_username:string, labels:[string], assignees:[string], discussion_count:int, unresolved_count:int, created_at_iso:string, updated_at_iso:string, web_url:string?, project_path:string, status_name:string?}]", "total_count": "int", "showing": "int"}, + "meta": {"elapsed_ms": "int", "available_statuses": "[string] — all distinct status names in the database, for use with --status filter"} }, "show": { "ok": "bool",