From 0361087876fc1ba68165189dc999f927a4a1451f Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter Date: Thu, 19 Feb 2026 18:54:26 -0700 Subject: [PATCH 1/3] feat: add comments delete command Enable commentDelete mutation in operations.toml and wire up the CLI command. Users can now delete comments by UUID. --- crates/lineark-sdk/src/generated/client_impl.rs | 4 ++++ crates/lineark-sdk/src/generated/mutations.rs | 11 +++++++++++ crates/lineark/src/commands/comments.rs | 16 ++++++++++++++++ crates/lineark/src/commands/usage.rs | 1 + schema/operations.toml | 1 + 5 files changed, 33 insertions(+) diff --git a/crates/lineark-sdk/src/generated/client_impl.rs b/crates/lineark-sdk/src/generated/client_impl.rs index 89cb2dc..bd65448 100644 --- a/crates/lineark-sdk/src/generated/client_impl.rs +++ b/crates/lineark-sdk/src/generated/client_impl.rs @@ -194,6 +194,10 @@ impl Client { ) -> Result { crate::generated::mutations::comment_create::(self, input).await } + /// Deletes a comment. + pub async fn comment_delete(&self, id: String) -> Result { + crate::generated::mutations::comment_delete(self, id).await + } /// Creates a new project. /// /// Full type: [`Project`](super::types::Project) diff --git a/crates/lineark-sdk/src/generated/mutations.rs b/crates/lineark-sdk/src/generated/mutations.rs index ce672fd..7fa403d 100644 --- a/crates/lineark-sdk/src/generated/mutations.rs +++ b/crates/lineark-sdk/src/generated/mutations.rs @@ -67,6 +67,17 @@ pub async fn comment_create< .execute_mutation::(&query, variables, "commentCreate", "comment") .await } +/// Deletes a comment. +pub async fn comment_delete(client: &Client, id: String) -> Result { + let variables = serde_json::json!({ "id" : id }); + let response_parts: Vec = vec!["success".to_string(), "entityId".to_string()]; + let query = String::from("mutation CommentDelete($id: String!) { commentDelete(id: $id) { ") + + &response_parts.join(" ") + + " } }"; + client + .execute::(&query, variables, "commentDelete") + .await +} /// Creates a new project. /// /// Full type: [`Project`](super::types::Project) diff --git a/crates/lineark/src/commands/comments.rs b/crates/lineark/src/commands/comments.rs index 3d4f350..84dd0a7 100644 --- a/crates/lineark/src/commands/comments.rs +++ b/crates/lineark/src/commands/comments.rs @@ -28,6 +28,14 @@ pub enum CommentsAction { #[arg(long)] body: String, }, + /// Delete a comment. + /// + /// Examples: + /// lineark comments delete + Delete { + /// Comment UUID. + id: String, + }, } /// Lean result type for comment mutations. @@ -58,6 +66,14 @@ pub async fn run(cmd: CommentsCmd, client: &Client, format: Format) -> anyhow::R output::print_one(&comment, format); } + CommentsAction::Delete { id } => { + client + .comment_delete(id) + .await + .map_err(|e| anyhow::anyhow!("{}", e))?; + + output::print_one(&serde_json::json!({ "success": true }), format); + } } Ok(()) } diff --git a/crates/lineark/src/commands/usage.rs b/crates/lineark/src/commands/usage.rs index 193a44d..84bab35 100644 --- a/crates/lineark/src/commands/usage.rs +++ b/crates/lineark/src/commands/usage.rs @@ -65,6 +65,7 @@ COMMANDS: lineark issues delete Delete (trash) an issue [--permanently] Permanently delete instead of trashing lineark comments create --body TEXT Comment on an issue + lineark comments delete Delete a comment lineark documents list [--limit N] List documents (lean output) [--project NAME-OR-ID] [--issue ID] Filter by project or issue lineark documents read Read document (includes content) diff --git a/schema/operations.toml b/schema/operations.toml index 7f2a786..9f56fcd 100644 --- a/schema/operations.toml +++ b/schema/operations.toml @@ -27,6 +27,7 @@ projectMilestone = true issueCreate = true issueUpdate = true commentCreate = true +commentDelete = true # Phase 3 — Issue lifecycle issueArchive = true From 29319b3eea2a06b1d3ab9abef2dd92f295e5b639 Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter Date: Thu, 19 Feb 2026 19:19:35 -0700 Subject: [PATCH 2/3] test: add offline tests for comments delete command --- crates/lineark/tests/offline.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/lineark/tests/offline.rs b/crates/lineark/tests/offline.rs index e6a65ad..68d5037 100644 --- a/crates/lineark/tests/offline.rs +++ b/crates/lineark/tests/offline.rs @@ -743,3 +743,32 @@ fn usage_includes_self_update_commands() { .stdout(predicate::str::contains("self update")) .stdout(predicate::str::contains("--check")); } + +// ── Comments delete ───────────────────────────────────────────────────────── + +#[test] +fn comments_help_shows_delete_subcommand() { + lineark() + .args(["comments", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("delete")); +} + +#[test] +fn comments_delete_help_shows_id_arg() { + lineark() + .args(["comments", "delete", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("")); +} + +#[test] +fn usage_includes_comments_delete() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("comments delete")); +} From a77e8860ecb5481056eca50569cc92663bef4f81 Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter Date: Thu, 19 Feb 2026 19:49:23 -0700 Subject: [PATCH 3/3] test: add online test for comments create and delete --- crates/lineark/tests/online.rs | 133 +++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/crates/lineark/tests/online.rs b/crates/lineark/tests/online.rs index 8ad221e..76f7b09 100644 --- a/crates/lineark/tests/online.rs +++ b/crates/lineark/tests/online.rs @@ -2508,4 +2508,137 @@ mod online { // Clean up: permanently delete the issue. delete_issue(issue_id); } + + // ── Comments create and delete ────────────────────────────────────────── + + #[test_with::runtime_ignore_if(no_online_test_token)] + fn comments_create_and_delete() { + let token = api_token(); + + // Get a team key. + let output = lineark() + .args(["--api-token", &token, "--format", "json", "teams", "list"]) + .output() + .unwrap(); + let teams: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + let team_key = teams[0]["key"].as_str().unwrap().to_string(); + + // Create an issue to comment on. + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "create", + "[test] CLI comments_delete", + "--team", + &team_key, + "--priority", + "4", + ]) + .output() + .unwrap(); + assert!(output.status.success(), "issue creation should succeed"); + let created: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + let issue_id = created["id"] + .as_str() + .expect("created issue should have id (UUID)") + .to_string(); + + // Create a comment. + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "comments", + "create", + &issue_id, + "--body", + "Comment that will be deleted.", + ]) + .output() + .unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "comment create should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + let comment: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + let comment_id = comment["id"] + .as_str() + .expect("comment should have an id") + .to_string(); + + // Delete the comment. + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "comments", + "delete", + &comment_id, + ]) + .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(), + "comments delete should succeed.\nstdout: {stdout}\nstderr: {stderr}" + ); + // Verify the delete response indicates success. + let delete_result: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + assert_eq!( + delete_result["success"].as_bool(), + Some(true), + "delete response should have success: true" + ); + + // Verify the comment is gone from the issue. + retry_with_backoff(8, || { + let output = lineark() + .args([ + "--api-token", + &token, + "--format", + "json", + "issues", + "read", + &issue_id, + ]) + .output() + .unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + if !output.status.success() { + return Err(format!("issues read failed: {stdout}")); + } + let detail: serde_json::Value = serde_json::from_str(&stdout).unwrap(); + let comments = detail + .get("comments") + .and_then(|c| c.get("nodes")) + .and_then(|n| n.as_array()); + let Some(comments) = comments else { + return Err("comments field missing".to_string()); + }; + let has_deleted = comments + .iter() + .any(|c| c["id"].as_str() == Some(&comment_id)); + if has_deleted { + Err("deleted comment still present".to_string()) + } else { + Ok(()) + } + }) + .expect("comment should be gone after deletion (after retries)"); + + // Clean up: permanently delete the issue. + delete_issue(&issue_id); + } }