From de49c33921fffe432246463f278ae0143a50e56d Mon Sep 17 00:00:00 2001 From: Stephen Way Date: Wed, 13 May 2026 08:29:31 -0700 Subject: [PATCH] batch: release, label, workflow, search, browse, status, org, keys, alias, config, extension, gist * New top-level groups, each with full CRUD where the API supports it: - release: list/view/create/edit/delete/upload/download - label: list/create/edit/delete - run: workflow runs (list/view/rerun/cancel) - secret + variable: Actions secrets/vars (list/set/delete) - search: cross-cutting (repos/issues/prs/users) - browse: open repo/path on the web - status: notifications inbox + mark-all-read - org: list/view/teams - ssh-key, gpg-key: list/add/delete on your account - alias: user-defined shortcuts (e.g. `fj alias set co "pr checkout"`) - config: local prefs (editor, pager, browser, etc.) - extension: discover and run `fj-` plugin binaries on PATH - gist: thin wrapper over `gist-*` repos * main.rs now expands aliases before clap and dispatches to plugins for unknown subcommands (matching gh). * New API modules: release, label, notification, search, org, workflow, with the corresponding strongly-typed wrappers. * Release asset upload uses reqwest multipart (feature flag added). Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 23 +++ Cargo.toml | 2 +- src/api/label.rs | 97 ++++++++++ src/api/mod.rs | 6 + src/api/notification.rs | 52 ++++++ src/api/org.rs | 44 +++++ src/api/release.rs | 163 +++++++++++++++++ src/api/repo.rs | 27 +-- src/api/search.rs | 45 +++++ src/api/workflow.rs | 172 ++++++++++++++++++ src/cli/alias.rs | 145 +++++++++++++++ src/cli/api.rs | 11 +- src/cli/browse.rs | 36 ++++ src/cli/config.rs | 134 ++++++++++++++ src/cli/extension.rs | 82 +++++++++ src/cli/gist.rs | 106 +++++++++++ src/cli/key.rs | 262 +++++++++++++++++++++++++++ src/cli/label.rs | 175 ++++++++++++++++++ src/cli/mod.rs | 61 +++++++ src/cli/org.rs | 115 ++++++++++++ src/cli/release.rs | 390 ++++++++++++++++++++++++++++++++++++++++ src/cli/repo.rs | 15 +- src/cli/search.rs | 123 +++++++++++++ src/cli/status.rs | 72 ++++++++ src/cli/workflow.rs | 310 ++++++++++++++++++++++++++++++++ src/client/mod.rs | 11 ++ src/main.rs | 82 ++++++++- 27 files changed, 2723 insertions(+), 38 deletions(-) create mode 100644 src/api/label.rs create mode 100644 src/api/notification.rs create mode 100644 src/api/org.rs create mode 100644 src/api/release.rs create mode 100644 src/api/search.rs create mode 100644 src/api/workflow.rs create mode 100644 src/cli/alias.rs create mode 100644 src/cli/browse.rs create mode 100644 src/cli/config.rs create mode 100644 src/cli/extension.rs create mode 100644 src/cli/gist.rs create mode 100644 src/cli/key.rs create mode 100644 src/cli/label.rs create mode 100644 src/cli/org.rs create mode 100644 src/cli/release.rs create mode 100644 src/cli/search.rs create mode 100644 src/cli/status.rs create mode 100644 src/cli/workflow.rs diff --git a/Cargo.lock b/Cargo.lock index 3fa187e..2999d67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -950,6 +950,22 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1240,6 +1256,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime_guess", "percent-encoding", "pin-project-lite", "quinn", @@ -1855,6 +1872,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index b6f3f2e..a5a47be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ thiserror = "2" clap = { version = "4.5", features = ["derive", "env", "wrap_help"] } clap_complete = "4.5" tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "process", "io-util", "io-std", "signal"] } -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream", "gzip", "brotli"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream", "gzip", "brotli", "multipart"] } serde = { version = "1", features = ["derive"] } serde_json = "1" toml = "0.8" diff --git a/src/api/label.rs b/src/api/label.rs new file mode 100644 index 0000000..7b5a8a6 --- /dev/null +++ b/src/api/label.rs @@ -0,0 +1,97 @@ +use anyhow::Result; +use reqwest::Method; +use serde::{Deserialize, Serialize}; + +use crate::client::Client; + +pub use super::issue::Label; + +#[derive(Debug, Clone, Serialize)] +pub struct CreateLabel<'a> { + pub name: &'a str, + pub color: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option<&'a str>, +} + +#[derive(Debug, Clone, Serialize, Default)] +pub struct EditLabel<'a> { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option<&'a str>, +} + +pub async fn list(client: &Client, owner: &str, name: &str) -> Result> { + let path = format!("/api/v1/repos/{owner}/{name}/labels"); + client.json(Method::GET, &path, &[], None::<&()>).await +} + +pub async fn create( + client: &Client, + owner: &str, + name: &str, + body: &CreateLabel<'_>, +) -> Result