ask-github 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 81872d8db5d4f3e8a63eed5fb7bb856daa386f447f522331d8c30b97400ad0a5
4
+ data.tar.gz: d86d708a8e4fbaac994a2cedaab52458434fae36b08f9c7cae98d8b70b9ae079
5
+ SHA512:
6
+ metadata.gz: 925160b0799b93e9c2665429c319594913a9346ab461ca9d14ba90809a7e264715c729f5513365681845f48f247ab468f6a91169361089f6346e52c910cf58e1
7
+ data.tar.gz: 0f44993f6e054c65512ddbed2a2ea0b2cef9e0436093e9edf0d224b55fbe94872964606ab0099eb0476036eb3adaa6a747e873f9cb2e3736d413f4eac2881c68
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - Unreleased
4
+
5
+ ### Added
6
+
7
+ - Initial release of `ask-github` — GitHub service context for the ask-rb ecosystem.
8
+ - **context.rb** — Metadata constants for AI system prompts: `DESCRIPTION`, `DOCS_URL`, `OPENAPI_URL`, `AUTH_NAME`, `AUTH_HOW`, `GEM_NAME`, `GEM_VERSION`, `GEM_DOCS`, `QUICK_START`
9
+ - **client.rb** — `Ask::GitHub.client` returns an authenticated Octokit client via `Ask::Auth.resolve(:github_token)`. Wraps client in `ClientProxy` to convert `Octokit::Unauthorized` to `Ask::Auth::InvalidCredential`.
10
+ - **error_guide.rb** — `Ask::GitHub::Errors` with rate limit info, HTTP status code descriptions, and exception guidance map for agents.
11
+ - **Dependencies:** `ask-auth ~> 0.1`, `octokit ~> 9.0`
12
+ - **Testing:** 26 tests, 69 assertions covering context constants, client auth flow, and error guide lookups.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaka Ruto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # ask-github
2
+
3
+ GitHub service context for AI agents in the ask-rb ecosystem.
4
+
5
+ Provides an authenticated Octokit client, metadata constants for system prompts, and a structured error guide for common GitHub API issues.
6
+
7
+ ```ruby
8
+ gem "ask-github"
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Get an authenticated client
14
+
15
+ ```ruby
16
+ require "ask-github"
17
+
18
+ client = Ask::GitHub.client
19
+ client.issues("owner/repo")
20
+ client.create_issue("owner/repo", "Title", "Body")
21
+ client.pull_requests("owner/repo")
22
+ client.contents("owner/repo", path: "Gemfile")
23
+ client.search_issues("query")
24
+ ```
25
+
26
+ The client is an `Octokit::Client` wrapped in a proxy that converts `Octokit::Unauthorized` into `Ask::Auth::InvalidCredential` with actionable error messages.
27
+
28
+ ### Authentication
29
+
30
+ The client resolves a GitHub token via `Ask::Auth.resolve(:github_token)`. Tokens can be provided through any configured auth provider:
31
+
32
+ 1. **Environment variable:** `GITHUB_TOKEN`
33
+ 2. **Credentials file:** `~/.ask/credentials.yml`
34
+ 3. **Rails credentials:** `Rails.application.credentials.github_token`
35
+ 4. **OAuth / Database:** Custom providers
36
+
37
+ Generate a token at [github.com/settings/tokens](https://github.com/settings/tokens) with the `repo` and `read:org` scopes.
38
+
39
+ ## Context Constants
40
+
41
+ Use these constants to build system prompts for AI agents:
42
+
43
+ | Constant | Value |
44
+ |---|---|
45
+ | `Ask::GitHub::DESCRIPTION` | "GitHub — code hosting, issues, pull requests, actions, packages" |
46
+ | `Ask::GitHub::DOCS_URL` | https://docs.github.com/en/rest |
47
+ | `Ask::GitHub::OPENAPI_URL` | https://api.github.com/openapi.json |
48
+ | `Ask::GitHub::AUTH_NAME` | `:github_token` |
49
+ | `Ask::GitHub::GEM_NAME` | `"octokit"` |
50
+ | `Ask::GitHub::GEM_VERSION` | `"~> 9.0"` |
51
+ | `Ask::GitHub::QUICK_START` | Copy-paste Ruby code snippet |
52
+
53
+ ## Error Guide
54
+
55
+ `Ask::GitHub::Errors` provides structured knowledge for agents:
56
+
57
+ ```ruby
58
+ Ask::GitHub::Errors.for("Octokit::NotFound")
59
+ # => { message: "...", action: "..." }
60
+
61
+ Ask::GitHub::Errors.status_code_description(404)
62
+ # => "Not Found — Resource does not exist or is private."
63
+
64
+ Ask::GitHub::Errors::RATE_LIMIT[:authenticated]
65
+ # => "5,000 requests per hour"
66
+ ```
67
+
68
+ ## Development
69
+
70
+ ```
71
+ bundle install
72
+ bundle exec rake test
73
+ ```
74
+
75
+ ## License
76
+
77
+ MIT
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "octokit"
4
+ require "ask/auth"
5
+
6
+ module Ask
7
+ module GitHub
8
+ # Returns an authenticated Octokit client configured for an AI agent.
9
+ #
10
+ # Resolves the GitHub token via +Ask::Auth.resolve(:github_token)+ and
11
+ # configures the client with sensible defaults:
12
+ #
13
+ # - +auto_paginate+: +true+ (collects all pages automatically)
14
+ # - +per_page+: +100+ (maximum items per page)
15
+ # - +middleware+: Faraday retry middleware (3 retries, exponential backoff)
16
+ #
17
+ # The client is wrapped in a +ClientProxy+ that converts
18
+ # +Octokit::Unauthorized+ into +Ask::Auth::InvalidCredential+.
19
+ #
20
+ # @example
21
+ # client = Ask::GitHub.client
22
+ # client.issues("owner/repo")
23
+ #
24
+ # @return [Octokit::Client] an authenticated client
25
+ # @raise [Ask::Auth::MissingCredential] if no GitHub token is configured
26
+ # @raise [Ask::Auth::InvalidCredential] if the token is rejected (401)
27
+ def self.client
28
+ token = Ask::Auth.resolve(:github_token)
29
+
30
+ client = Octokit::Client.new(access_token: token, auto_paginate: true, per_page: 100)
31
+
32
+ # Configure Faraday retry middleware for transient failures
33
+ client.middleware = Faraday::RackBuilder.new do |builder|
34
+ builder.request :retry, max: 3, interval: 1, backoff_factor: 2,
35
+ retry_statuses: [429, 500, 502, 503]
36
+ builder.adapter Faraday.default_adapter
37
+ end
38
+
39
+ ClientProxy.new(client)
40
+ end
41
+
42
+ # Proxies method calls to an +Octokit::Client+, converting authentication
43
+ # errors into +Ask::Auth::InvalidCredential+.
44
+ class ClientProxy < BasicObject
45
+ def initialize(client)
46
+ @client = client
47
+ end
48
+
49
+ def method_missing(name, ...)
50
+ @client.public_send(name, ...)
51
+ rescue ::Octokit::Unauthorized
52
+ ::Kernel.raise ::Ask::Auth::InvalidCredential, :github_token
53
+ end
54
+
55
+ def respond_to_missing?(name, include_private = false)
56
+ @client.respond_to?(name, include_private) || super
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module GitHub
5
+ # Human-readable description of the GitHub service context.
6
+ DESCRIPTION = "GitHub — code hosting, issues, pull requests, actions, packages"
7
+
8
+ # Base URL for GitHub REST API documentation.
9
+ DOCS_URL = "https://docs.github.com/en/rest"
10
+
11
+ # URL for the GitHub OpenAPI specification.
12
+ OPENAPI_URL = "https://api.github.com/openapi.json"
13
+
14
+ # Credential name used with Ask::Auth.resolve.
15
+ AUTH_NAME = :github_token
16
+
17
+ # Instructions for obtaining a GitHub personal access token.
18
+ AUTH_HOW = "https://github.com/settings/tokens — scopes: repo, read:org"
19
+
20
+ # Gem name for the GitHub API client.
21
+ GEM_NAME = "octokit"
22
+
23
+ # Required gem version constraint.
24
+ GEM_VERSION = "~> 9.0"
25
+
26
+ # URL for Octokit Ruby library documentation.
27
+ GEM_DOCS = "https://octokit.github.io/octokit.rb"
28
+
29
+ # Quick-start Ruby code snippet for agents to copy-paste.
30
+ QUICK_START = <<~RUBY
31
+ client = Ask::GitHub.client
32
+ client.issues("owner/repo")
33
+ client.create_issue("owner/repo", "Title", "Body")
34
+ client.pull_requests("owner/repo")
35
+ client.contents("owner/repo", path: "Gemfile")
36
+ client.search_issues("query")
37
+ RUBY
38
+ end
39
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module GitHub
5
+ # Structured error knowledge for AI agents working with the GitHub API.
6
+ #
7
+ # Provides human-readable guidance for common HTTP status codes, rate
8
+ # limiting, pagination, and authentication errors encountered when
9
+ # using the Octokit client.
10
+ module Errors
11
+ # Rate limit information.
12
+ #
13
+ # - Authenticated requests: 5,000 requests per hour
14
+ # - Unauthenticated requests: 60 requests per hour
15
+ #
16
+ # When rate-limited, +Octokit::TooManyRequests+ is raised. The agent
17
+ # should wait for the reset time and retry.
18
+ RATE_LIMIT = {
19
+ authenticated: "5,000 requests per hour (using personal access token)",
20
+ anonymous: "60 requests per hour (no authentication)",
21
+ error_class: "Octokit::TooManyRequests",
22
+ action: "Wait for the rate limit reset window, then retry the request."
23
+ }.freeze
24
+
25
+ # Common HTTP status codes returned by the GitHub API and how to handle them.
26
+ STATUS_CODES = {
27
+ 200 => "OK — Request succeeded.",
28
+ 201 => "Created — Resource was created successfully.",
29
+ 202 => "Accepted — Request accepted for processing (async operations).",
30
+ 204 => "No Content — Request succeeded, no response body.",
31
+ 301 => "Moved Permanently — The resource URL has changed; update your reference.",
32
+ 304 => "Not Modified — Cached response is still valid (conditional request).",
33
+ 401 => "Unauthorized — Token is missing, invalid, or revoked. Re-authenticate.",
34
+ 403 => "Forbidden — Token lacks required scopes. Check token permissions.",
35
+ 404 => "Not Found — Resource does not exist or is private.",
36
+ 409 => "Conflict — Resource state conflict (e.g., merge conflict).",
37
+ 410 => "Gone — Resource was removed permanently.",
38
+ 422 => "Unprocessable Entity — Validation failed. Check request parameters.",
39
+ 429 => "Too Many Requests — Rate limit exceeded. Wait before retrying.",
40
+ 500 => "Internal Server Error — GitHub server issue. Retry with backoff.",
41
+ 502 => "Bad Gateway — GitHub upstream issue. Retry with backoff.",
42
+ 503 => "Service Unavailable — GitHub is down for maintenance. Retry later."
43
+ }.freeze
44
+
45
+ # Pagination guidance for large result sets.
46
+ PAGINATION = {
47
+ auto_paginate: "Octokit::Client is configured with auto_paginate: true by default.",
48
+ per_page: "Maximum items per page is 100. The default client uses 100.",
49
+ link_header: "GitHub uses Link headers for pagination. Octokit handles this automatically.",
50
+ max_results: "For very large queries, iterate pages or use GitHub's search API."
51
+ }.freeze
52
+
53
+ # Map of Octokit exception classes to human-readable guidance.
54
+ EXCEPTIONS = {
55
+ "Octokit::Unauthorized" => {
56
+ message: "Your GitHub token is invalid or has been revoked.",
57
+ action: "Generate a new token at https://github.com/settings/tokens with the 'repo' and 'read:org' scopes."
58
+ },
59
+ "Octokit::Forbidden" => {
60
+ message: "Your token lacks the required permissions for this operation.",
61
+ action: "Check your token scopes at https://github.com/settings/tokens. You may need additional scopes."
62
+ },
63
+ "Octokit::NotFound" => {
64
+ message: "The requested repository, issue, or resource does not exist or is private.",
65
+ action: "Verify the owner/repo name and that the repository is accessible with your token."
66
+ },
67
+ "Octokit::TooManyRequests" => {
68
+ message: "GitHub API rate limit exceeded.",
69
+ action: "Check X-RateLimit-Reset header, wait until that timestamp, then retry. Authenticated requests get 5,000/hr."
70
+ },
71
+ "Octokit::UnprocessableEntity" => {
72
+ message: "The request body failed validation.",
73
+ action: "Check required fields, data types, and constraints. GitHub returns detailed error messages in the response body."
74
+ },
75
+ "Octokit::ServerError" => {
76
+ message: "GitHub encountered a server error.",
77
+ action: "Retry with exponential backoff. If the issue persists, check https://www.githubstatus.com."
78
+ },
79
+ "Octokit::InvalidRepository" => {
80
+ message: "The repository name format is invalid.",
81
+ action: "Use the format 'owner/repo' (e.g., 'octocat/hello-world')."
82
+ }
83
+ }.freeze
84
+
85
+ # Look up guidance for an exception class name.
86
+ #
87
+ # @param exception_class [String] The exception class name (e.g., "Octokit::NotFound")
88
+ # @return [Hash, nil] A hash with +:message+ and +:action+ keys, or nil if unknown
89
+ def self.for(exception_class)
90
+ EXCEPTIONS[exception_class]
91
+ end
92
+
93
+ # Describe an HTTP status code.
94
+ #
95
+ # @param code [Integer] HTTP status code
96
+ # @return [String, nil] Description of the status code
97
+ def self.status_code_description(code)
98
+ STATUS_CODES[code]
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module GitHub
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
data/lib/ask-github.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/github/version"
4
+ require_relative "ask/github/context"
5
+ require_relative "ask/github/client"
6
+ require_relative "ask/github/error_guide"
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-github
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ask-auth
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: octokit
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '9.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '9.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: faraday-retry
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.2'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.2'
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '5.25'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.25'
68
+ - !ruby/object:Gem::Dependency
69
+ name: mocha
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.1'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: vcr
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '6.3'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '6.3'
96
+ - !ruby/object:Gem::Dependency
97
+ name: webmock
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.23'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.23'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rake
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '13.0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '13.0'
124
+ description: Provides authenticated Octokit client, context metadata, and error guide
125
+ for AI agents.
126
+ email:
127
+ - kaka@myrrlabs.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - CHANGELOG.md
133
+ - LICENSE
134
+ - README.md
135
+ - lib/ask-github.rb
136
+ - lib/ask/github/client.rb
137
+ - lib/ask/github/context.rb
138
+ - lib/ask/github/error_guide.rb
139
+ - lib/ask/github/version.rb
140
+ homepage: https://github.com/ask-rb/ask-github
141
+ licenses:
142
+ - MIT
143
+ metadata:
144
+ homepage_uri: https://github.com/ask-rb/ask-github
145
+ source_code_uri: https://github.com/ask-rb/ask-github
146
+ changelog_uri: https://github.com/ask-rb/ask-github/blob/main/CHANGELOG.md
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '3.2'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubygems_version: 4.0.3
162
+ specification_version: 4
163
+ summary: GitHub service context for the ask-rb ecosystem
164
+ test_files: []