Add CLI comment, tag, and blocker commands
Assisted-by: Claude Opus 4.6 via pi
change mpsnpwrtpmkkppwpwormlyvpvxnpyymp
commit 12f5b7b343d2dd9aa1ac686df74be52b4ab94f96
author Alpha Chen <alpha@kejadlen.dev>
date
parent pyrtxpmx
diff --git a/crates/ranger-cli/src/commands/blocker.rs b/crates/ranger-cli/src/commands/blocker.rs
new file mode 100644
index 0000000..a6bebfa
--- /dev/null
+++ b/crates/ranger-cli/src/commands/blocker.rs
@@ -0,0 +1,48 @@
+use clap::Subcommand;
+use ranger_lib::db::SqlitePool;
+use ranger_lib::ops;
+
+use crate::output;
+
+#[derive(Subcommand)]
+pub enum BlockerCommands {
+    /// Add a blocker to a task
+    Add {
+        /// Task key or prefix (the blocked task)
+        task: String,
+        /// Blocking task key or prefix
+        blocked_by: String,
+    },
+    /// Remove a blocker from a task
+    Remove {
+        /// Task key or prefix (the blocked task)
+        task: String,
+        /// Blocking task key or prefix
+        blocked_by: String,
+    },
+}
+
+pub async fn run(pool: &SqlitePool, command: BlockerCommands, json: bool) -> anyhow::Result<()> {
+    match command {
+        BlockerCommands::Add { task, blocked_by } => {
+            let t = ops::task::get_by_key_prefix(pool, &task).await?;
+            let bt = ops::task::get_by_key_prefix(pool, &blocked_by).await?;
+            let blocker = ops::blocker::add(pool, t.id, bt.id).await?;
+            output::print(&blocker, json, |_| {
+                println!(
+                    "{} blocked by {} {}",
+                    &t.key[..8],
+                    &bt.key[..8],
+                    bt.title
+                );
+            });
+        }
+        BlockerCommands::Remove { task, blocked_by } => {
+            let t = ops::task::get_by_key_prefix(pool, &task).await?;
+            let bt = ops::task::get_by_key_prefix(pool, &blocked_by).await?;
+            ops::blocker::remove(pool, t.id, bt.id).await?;
+            println!("Removed blocker {} from {}", &bt.key[..8], &t.key[..8]);
+        }
+    }
+    Ok(())
+}
diff --git a/crates/ranger-cli/src/commands/comment.rs b/crates/ranger-cli/src/commands/comment.rs
new file mode 100644
index 0000000..134822f
--- /dev/null
+++ b/crates/ranger-cli/src/commands/comment.rs
@@ -0,0 +1,41 @@
+use clap::Subcommand;
+use ranger_lib::db::SqlitePool;
+use ranger_lib::ops;
+
+use crate::output;
+
+#[derive(Subcommand)]
+pub enum CommentCommands {
+    /// Add a comment to a task
+    Add {
+        /// Task key or prefix
+        task: String,
+        /// Comment body
+        body: String,
+    },
+    /// List comments on a task
+    List {
+        /// Task key or prefix
+        task: String,
+    },
+}
+
+pub async fn run(pool: &SqlitePool, command: CommentCommands, json: bool) -> anyhow::Result<()> {
+    match command {
+        CommentCommands::Add { task, body } => {
+            let t = ops::task::get_by_key_prefix(pool, &task).await?;
+            let comment = ops::comment::add(pool, t.id, &body).await?;
+            output::print(&comment, json, |c| {
+                println!("[{}] {}", c.created_at, c.body);
+            });
+        }
+        CommentCommands::List { task } => {
+            let t = ops::task::get_by_key_prefix(pool, &task).await?;
+            let comments = ops::comment::list(pool, t.id).await?;
+            output::print_list(&comments, json, |c| {
+                println!("[{}] {}", c.created_at, c.body);
+            });
+        }
+    }
+    Ok(())
+}
diff --git a/crates/ranger-cli/src/commands/mod.rs b/crates/ranger-cli/src/commands/mod.rs
index 5381e93..3674cef 100644
--- a/crates/ranger-cli/src/commands/mod.rs
+++ b/crates/ranger-cli/src/commands/mod.rs
@@ -1,2 +1,5 @@
 pub mod backlog;
+pub mod blocker;
+pub mod comment;
+pub mod tag;
 pub mod task;
diff --git a/crates/ranger-cli/src/commands/tag.rs b/crates/ranger-cli/src/commands/tag.rs
new file mode 100644
index 0000000..8c87496
--- /dev/null
+++ b/crates/ranger-cli/src/commands/tag.rs
@@ -0,0 +1,23 @@
+use clap::Subcommand;
+use ranger_lib::db::SqlitePool;
+use ranger_lib::ops;
+
+use crate::output;
+
+#[derive(Subcommand)]
+pub enum TagCommands {
+    /// List all tags
+    List,
+}
+
+pub async fn run(pool: &SqlitePool, command: TagCommands, json: bool) -> anyhow::Result<()> {
+    match command {
+        TagCommands::List => {
+            let tags = ops::tag::list(pool).await?;
+            output::print_list(&tags, json, |t| {
+                println!("{}", t.name);
+            });
+        }
+    }
+    Ok(())
+}
diff --git a/crates/ranger-cli/src/main.rs b/crates/ranger-cli/src/main.rs
index 53cf556..0fa6f48 100644
--- a/crates/ranger-cli/src/main.rs
+++ b/crates/ranger-cli/src/main.rs
@@ -31,6 +31,21 @@ enum Commands {
         #[command(subcommand)]
         command: commands::task::TaskCommands,
     },
+    /// Manage comments
+    Comment {
+        #[command(subcommand)]
+        command: commands::comment::CommentCommands,
+    },
+    /// Manage tags
+    Tag {
+        #[command(subcommand)]
+        command: commands::tag::TagCommands,
+    },
+    /// Manage blockers
+    Blocker {
+        #[command(subcommand)]
+        command: commands::blocker::BlockerCommands,
+    },
 }
 
 fn resolve_db_path(cli_path: Option<PathBuf>) -> PathBuf {
@@ -55,6 +70,15 @@ async fn main() -> anyhow::Result<()> {
         Commands::Task { command } => {
             commands::task::run(&pool, command, cli.json).await?;
         }
+        Commands::Comment { command } => {
+            commands::comment::run(&pool, command, cli.json).await?;
+        }
+        Commands::Tag { command } => {
+            commands::tag::run(&pool, command, cli.json).await?;
+        }
+        Commands::Blocker { command } => {
+            commands::blocker::run(&pool, command, cli.json).await?;
+        }
     }
 
     Ok(())