From 7b4386d4b634336bef7b60ac530e401afab9ef97 Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter Date: Thu, 19 Feb 2026 19:23:57 -0700 Subject: [PATCH 1/4] feat: add --estimate flag to issues create and update --- crates/lineark/src/commands/issues.rs | 13 ++++++++++- crates/lineark/src/commands/usage.rs | 5 +++-- crates/lineark/tests/offline.rs | 31 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/crates/lineark/src/commands/issues.rs b/crates/lineark/src/commands/issues.rs index 55c9ae2..2fa5728 100644 --- a/crates/lineark/src/commands/issues.rs +++ b/crates/lineark/src/commands/issues.rs @@ -90,6 +90,9 @@ pub enum IssuesAction { /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] priority: Option, + /// Estimate points (valid values depend on the team's estimation scale). + #[arg(short = 'e', long)] + estimate: Option, /// Issue description (markdown). #[arg(short = 'd', long)] description: Option, @@ -149,6 +152,9 @@ pub enum IssuesAction { /// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low. #[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))] priority: Option, + /// Estimate points (valid values depend on the team's estimation scale). + #[arg(short = 'e', long)] + estimate: Option, /// Comma-separated label names or UUIDs. Behavior depends on --label-by. #[arg(long, value_delimiter = ',')] labels: Option>, @@ -551,6 +557,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res assignee, labels, priority, + estimate, description, parent, status, @@ -595,6 +602,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res assignee_id, label_ids, priority, + estimate, description, parent_id, state_id, @@ -648,6 +656,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res identifier, status, priority, + estimate, labels, label_by, clear_labels, @@ -661,6 +670,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res } => { if status.is_none() && priority.is_none() + && estimate.is_none() && labels.is_none() && !clear_labels && assignee.is_none() @@ -672,7 +682,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res && cycle.is_none() { return Err(anyhow::anyhow!( - "No update fields provided. Use --status, --priority, --assignee, --labels, --title, --description, --parent, --project, or --cycle to specify changes." + "No update fields provided. Use --status, --priority, --estimate, --assignee, --labels, --title, --description, --parent, --project, or --cycle to specify changes." )); } @@ -744,6 +754,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res description, assignee_id, priority, + estimate, state_id, parent_id, label_ids, diff --git a/crates/lineark/src/commands/usage.rs b/crates/lineark/src/commands/usage.rs index 40109d5..047b4e8 100644 --- a/crates/lineark/src/commands/usage.rs +++ b/crates/lineark/src/commands/usage.rs @@ -62,12 +62,13 @@ COMMANDS: [--team KEY] [--assignee NAME-OR-ID|me] Filter by team, assignee, status [--status NAME,...] [--show-done] Comma-separated status names lineark issues create --team KEY Create an issue - [-p 0-4] [--assignee NAME-OR-ID|me] 0=none 1=urgent 2=high 3=medium 4=low + [-p 0-4] [-e N] [--assignee NAME-OR-ID|me] 0=none 1=urgent 2=high 3=medium 4=low [--labels NAME,...] [-d TEXT] [-s NAME] Label names (team-scoped), status name [--parent ID] [--project NAME-OR-ID] Parent issue, project, cycle [--cycle NAME-OR-ID] lineark issues update <IDENTIFIER> Update an issue - [-s NAME] [-p 0-4] [--assignee NAME-OR-ID|me] Status, priority, assignee + [-s NAME] [-p 0-4] [-e N] Status, priority, estimate + [--assignee NAME-OR-ID|me] Assignee [--labels NAME,...] [--label-by adding|replacing|removing] [--clear-labels] [-t TEXT] [-d TEXT] Title, description [--parent ID] [--clear-parent] Set or remove parent diff --git a/crates/lineark/tests/offline.rs b/crates/lineark/tests/offline.rs index d916b73..c468f4a 100644 --- a/crates/lineark/tests/offline.rs +++ b/crates/lineark/tests/offline.rs @@ -907,3 +907,34 @@ fn usage_includes_issues_list_project_filter() { .success() .stdout(predicate::str::contains("--project")); } + +// ── Estimate flag ─────────────────────────────────────────────────────────── + +#[test] +fn issues_create_help_shows_estimate() { + lineark() + .args(["issues", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--estimate")) + .stdout(predicate::str::contains("-e")); +} + +#[test] +fn issues_update_help_shows_estimate() { + lineark() + .args(["issues", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--estimate")) + .stdout(predicate::str::contains("-e")); +} + +#[test] +fn issues_update_no_flags_error_mentions_estimate() { + lineark() + .args(["--api-token", "fake-token", "issues", "update", "ENG-123"]) + .assert() + .failure() + .stderr(predicate::str::contains("--estimate")) +} From 76b2c9dd50eb52288f0838c21b2e1a5da0bb7787 Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter <geoffrey@lightstrike.co> Date: Thu, 19 Feb 2026 19:34:38 -0700 Subject: [PATCH 2/4] test: add online test for issues create/update with --estimate --- crates/lineark/tests/online.rs | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/crates/lineark/tests/online.rs b/crates/lineark/tests/online.rs index f06f897..3f38d75 100644 --- a/crates/lineark/tests/online.rs +++ b/crates/lineark/tests/online.rs @@ -3592,4 +3592,86 @@ mod online { }) .expect("issues list --project should return at least one issue"); } + + // ── Issues create with --estimate ─────────────────────────────────────── + + #[test_with::runtime_ignore_if(no_online_test_token)] + fn issues_create_with_estimate() { + let token = api_token(); + let unique_name = format!( + "[test] CLI estimate flag {}", + &uuid::Uuid::new_v4().to_string()[..8] + ); + + // Get a team key. + let output = lineark() + .args(["--api-token", &token, "--format", "json", "teams", "list"]) + .output() + .unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "teams list should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + let teams: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + let team_key = teams[0]["key"].as_str().unwrap().to_string(); + + // Create an issue with --estimate. + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "create", + &unique_name, + "--team", + &team_key, + "--priority", + "4", + "--estimate", + "3", + ]) + .output() + .expect("failed to execute lineark"); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "issues create --estimate should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + let created: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + let issue_id = created["id"] + .as_str() + .expect("created issue should have id") + .to_string(); + let _issue_guard = IssueGuard { + token: token.clone(), + id: issue_id.clone(), + }; + + // Update the issue with a different estimate. + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "update", + &issue_id, + "--estimate", + "5", + ]) + .output() + .expect("failed to execute lineark"); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "issues update --estimate should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + } } From 17a66e503b4882be7e39c22698b90329773b728f Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter <geoffrey@lightstrike.co> Date: Sat, 28 Feb 2026 13:13:57 -0700 Subject: [PATCH 3/4] fix: add missing semicolon in offline test from rebase conflict resolution --- crates/lineark/tests/offline.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lineark/tests/offline.rs b/crates/lineark/tests/offline.rs index c468f4a..4fe0253 100644 --- a/crates/lineark/tests/offline.rs +++ b/crates/lineark/tests/offline.rs @@ -936,5 +936,5 @@ fn issues_update_no_flags_error_mentions_estimate() { .args(["--api-token", "fake-token", "issues", "update", "ENG-123"]) .assert() .failure() - .stderr(predicate::str::contains("--estimate")) + .stderr(predicate::str::contains("--estimate")); } From 33ac1c932fe936629908bd4b44b3c78322dd30f7 Mon Sep 17 00:00:00 2001 From: Cadu <cadu.coelho@gmail.com> Date: Fri, 6 Mar 2026 19:13:26 +0100 Subject: [PATCH 4/4] feat: show estimate field in issues read output Add estimate to IssueDetail so it appears in `issues read` JSON and human output. Without this, setting --estimate on create/update had no way to be verified via the CLI. --- crates/lineark/src/commands/issues.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/lineark/src/commands/issues.rs b/crates/lineark/src/commands/issues.rs index 2fa5728..966a083 100644 --- a/crates/lineark/src/commands/issues.rs +++ b/crates/lineark/src/commands/issues.rs @@ -322,6 +322,7 @@ pub struct IssueDetail { pub description: Option<String>, pub priority: Option<f64>, pub priority_label: Option<String>, + pub estimate: Option<f64>, pub url: Option<String>, pub created_at: Option<String>, pub updated_at: Option<String>,