diff --git a/Cargo.lock b/Cargo.lock
index f924467a..ecabd202 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1105,6 +1105,12 @@ dependencies = [
"cc",
]
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
[[package]]
name = "log"
version = "0.4.17"
@@ -1577,7 +1583,9 @@ dependencies = [
"radicle",
"radicle-cob",
"radicle-crypto",
+ "serde",
"serde_json",
+ "serde_yaml",
"thiserror",
"timeago",
"zeroize",
@@ -1945,6 +1953,18 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_yaml"
+version = "0.8.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
+dependencies = [
+ "indexmap",
+ "ryu",
+ "serde",
+ "yaml-rust",
+]
+
[[package]]
name = "sha2"
version = "0.9.9"
@@ -2695,6 +2715,15 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
[[package]]
name = "zeroize"
version = "1.5.7"
diff --git a/radicle-cli/Cargo.toml b/radicle-cli/Cargo.toml
index e0357bf8..93429173 100644
--- a/radicle-cli/Cargo.toml
+++ b/radicle-cli/Cargo.toml
@@ -14,7 +14,9 @@ indicatif = { version = "0.16.2" }
json-color = { version = "0.7" }
lexopt = { version = "0.2" }
log = { version = "0.4", features = ["std"] }
+serde = { version = "1.0" }
serde_json = { version = "1" }
+serde_yaml = { version = "0.8" }
thiserror = { version = "1" }
timeago = { version = "0.3", default-features = false }
zeroize = { version = "1.1" }
diff --git a/radicle-cli/src/commands.rs b/radicle-cli/src/commands.rs
index 46c3922c..5bc61e1f 100644
--- a/radicle-cli/src/commands.rs
+++ b/radicle-cli/src/commands.rs
@@ -12,6 +12,8 @@ pub mod rad_help;
pub mod rad_init;
#[path = "commands/inspect.rs"]
pub mod rad_inspect;
+#[path = "commands/issue.rs"]
+pub mod rad_issue;
#[path = "commands/ls.rs"]
pub mod rad_ls;
#[path = "commands/patch.rs"]
diff --git a/radicle-cli/src/commands/help.rs b/radicle-cli/src/commands/help.rs
index 41a5179a..25458dae 100644
--- a/radicle-cli/src/commands/help.rs
+++ b/radicle-cli/src/commands/help.rs
@@ -23,6 +23,7 @@ const COMMANDS: &[Help] = &[
rad_edit::HELP,
rad_inspect::HELP,
rad_rm::HELP,
+ rad_issue::HELP,
HELP,
];
diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs
new file mode 100644
index 00000000..afac4bbd
--- /dev/null
+++ b/radicle-cli/src/commands/issue.rs
@@ -0,0 +1,247 @@
+#![allow(clippy::or_fun_call)]
+use std::ffi::OsString;
+use std::str::FromStr;
+
+use anyhow::{anyhow, Context as _};
+
+use crate::terminal as term;
+use crate::terminal::args::{Args, Error, Help};
+
+use radicle::cob::issue::{CloseReason, IssueId, State};
+use radicle::cob::shared::{Label, Reaction};
+use radicle::cob::store::Store;
+use radicle::storage::WriteStorage;
+
+pub const HELP: Help = Help {
+ name: "issue",
+ description: "Manage radicle issues",
+ version: env!("CARGO_PKG_VERSION"),
+ usage: r#"
+Usage
+
+ rad issue new [--title
] [--description ]
+ rad issue state [--closed | --open | --solved]
+ rad issue delete
+ rad issue react [--emoji ]
+ rad issue list
+
+Options
+
+ --help Print help
+"#,
+};
+
+#[derive(serde::Deserialize, serde::Serialize, Debug)]
+pub struct Metadata {
+ title: String,
+ labels: Vec