diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e2d8a7..0234bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.4] - 2026-05-07 + +### Changed + +- GitHub API authentication now reads `GH_TOKEN` in addition to `GITHUB_TOKEN`, + matching the `gh` CLI convention. `GH_TOKEN` takes precedence when both are + set. (#67) + ## [1.0.0] - 2026-03-24 ### Added diff --git a/Cargo.lock b/Cargo.lock index cfe8d34..608eee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1516,7 +1516,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "skillshub" -version = "1.0.3" +version = "1.0.4" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b25e452..4249c42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "skillshub" -version = "1.0.3" +version = "1.0.4" edition = "2021" rust-version = "1.74.0" description = "A package manager for AI coding agent skills - like homebrew for skills" diff --git a/README.md b/README.md index daf759b..db64556 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,9 @@ skillshub star-list https://github.com/stars/username/lists/skills skillshub star-list https://github.com/stars/username/lists/skills --install ``` -This requires a `GITHUB_TOKEN` (the GraphQL API requires authentication). +This requires `GH_TOKEN` or `GITHUB_TOKEN` (the GraphQL API requires +authentication). `GH_TOKEN` takes precedence when both are set, matching the +`gh` CLI convention. ### Agent Linking @@ -222,9 +224,12 @@ The GitHub API is only used for: - **Gist skills** (`skillshub add https://gist.github.com/...`) - **Star list imports** (`skillshub star-list ...`) -For these operations, set a `GITHUB_TOKEN` to avoid rate limiting: +For these operations, set `GH_TOKEN` (preferred) or `GITHUB_TOKEN` to avoid +rate limiting. When both are set, `GH_TOKEN` wins, matching the `gh` CLI: ```bash +export GH_TOKEN=your_token_here +# or export GITHUB_TOKEN=your_token_here ``` diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 5a88c20..b7cb2fb 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -23,7 +23,8 @@ skillshub star-list # Add all repos from a star list as skillshub star-list --install # Also install all skills from each tap ``` -Requires `GITHUB_TOKEN` (GraphQL API requires authentication). +Requires `GH_TOKEN` or `GITHUB_TOKEN` (GraphQL API requires authentication). +`GH_TOKEN` is checked first, matching the `gh` CLI. ## Tap Management diff --git a/src/registry/github.rs b/src/registry/github.rs index ed16efd..b7d5856 100644 --- a/src/registry/github.rs +++ b/src/registry/github.rs @@ -113,8 +113,8 @@ fn print_rate_limit_wait(reason: &str, wait_secs: u64, attempt: u32) { " {} Waiting {}s before retrying (attempt {}/{})...", reason, wait_secs, attempt, MAX_RETRIES ); - if std::env::var("GITHUB_TOKEN").is_err() { - eprint!("\n Tip: Set GITHUB_TOKEN for higher rate limits (5000/hour vs 60/hour)."); + if github_token().is_none() { + eprint!("\n Tip: Set GH_TOKEN or GITHUB_TOKEN for higher rate limits (5000/hour vs 60/hour)."); } eprintln!(); } @@ -164,7 +164,7 @@ where if let Some(wait) = rate_info.wait_duration() { if wait.as_secs() > MAX_RATE_LIMIT_WAIT_SECS { anyhow::bail!( - "Rate limit reset is {}s away (>{} max). Set GITHUB_TOKEN for higher limits.", + "Rate limit reset is {}s away (>{} max). Set GH_TOKEN or GITHUB_TOKEN for higher limits.", wait.as_secs(), MAX_RATE_LIMIT_WAIT_SECS ); @@ -267,9 +267,24 @@ fn build_client() -> Result { }) } -/// Add GitHub token authentication to a request if GITHUB_TOKEN is set +/// Read the GitHub auth token from the environment. +/// +/// Checks `GH_TOKEN` first (matching the `gh` CLI convention), then falls +/// back to `GITHUB_TOKEN`. Empty values are treated as unset. +fn github_token() -> Option { + for var in ["GH_TOKEN", "GITHUB_TOKEN"] { + if let Ok(token) = std::env::var(var) { + if !token.is_empty() { + return Some(token); + } + } + } + None +} + +/// Add GitHub token authentication to a request if a token env var is set. fn with_auth(request: RequestBuilder) -> RequestBuilder { - if let Ok(token) = std::env::var("GITHUB_TOKEN") { + if let Some(token) = github_token() { request.bearer_auth(token) } else { request @@ -337,7 +352,7 @@ pub fn get_default_branch(owner: &str, repo: &str) -> Result { "Repository not found on GitHub: {}/{}\n\ Please check that:\n\ - The repository exists and is spelled correctly\n\ - - The repository is public (or GITHUB_TOKEN is set for private repos)", + - The repository is public (or GH_TOKEN/GITHUB_TOKEN is set for private repos)", owner, repo ); @@ -457,7 +472,7 @@ fn is_valid_repo_id(s: &str) -> bool { /// /// Uses the GitHub Tree API to recursively find all SKILL.md files in the repo, /// then fetches each one to extract metadata. -/// Set GITHUB_TOKEN environment variable to avoid rate limiting. +/// Set `GH_TOKEN` or `GITHUB_TOKEN` environment variable to avoid rate limiting. pub fn discover_skills_from_repo(github_url: &GitHubUrl, tap_name: &str) -> Result { let client = build_client()?; @@ -632,7 +647,7 @@ pub fn fetch_gist(gist_id: &str) -> Result { anyhow::bail!( "Gist not found: {}\n\ Please check that the gist ID is correct and the gist is public \ - (or GITHUB_TOKEN is set for secret gists)", + (or GH_TOKEN/GITHUB_TOKEN is set for secret gists)", gist_id ); } @@ -733,11 +748,11 @@ pub fn parse_star_list_url(url: &str) -> Result<(String, String)> { /// /// Returns a list of "owner/repo" identifiers. pub fn fetch_star_list_repos(username: &str, list_name: &str) -> Result> { - let token = std::env::var("GITHUB_TOKEN").with_context(|| { - "GITHUB_TOKEN is required for star list operations.\n\ + let token = github_token().context( + "GH_TOKEN or GITHUB_TOKEN is required for star list operations.\n\ The GraphQL API does not support unauthenticated requests.\n\ - Set GITHUB_TOKEN with a personal access token." - })?; + Set GH_TOKEN (preferred) or GITHUB_TOKEN with a personal access token.", + )?; let client = build_client()?; let gql_url = graphql_url(); @@ -768,7 +783,7 @@ pub fn fetch_star_list_repos(username: &str, list_name: &str) -> Result