ask-linear 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: 2cdf635069d34d0554d7e73ff22a0b589627ab59ace75b8376cfff7b11a502d5
4
+ data.tar.gz: 81cc288e84cb63fb8b622032adbdc911844cbe29097d2b99f5d0a19548fc8f69
5
+ SHA512:
6
+ metadata.gz: 75e4a052079f42fdb445fcaea78010597c19708a5872a399c970b0f28f72c0ce77a76c0b6df4fe9d2c5681a060cff28cba750fd8de999e6438f745297c7ce87f
7
+ data.tar.gz: e4e4cb88175a66383b416409f85e05ad2661168f28df182dd82c230429d3ec1b2fef341886f12255a3434af258d74bfc1a40beeff104298f6e4d59b53bbcd631
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,157 @@
1
+ # ask-linear
2
+
3
+ Linear service context for AI agents in the ask-rb ecosystem.
4
+
5
+ Provides an authenticated GraphQL client, metadata constants for system prompts, and a structured error guide for common Linear API issues.
6
+
7
+ ```ruby
8
+ gem "ask-linear"
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Get an authenticated client
14
+
15
+ ```ruby
16
+ require "ask-linear"
17
+
18
+ client = Ask::Linear.client
19
+
20
+ # List all teams
21
+ result = client.query("query { teams { nodes { id key name } } }")
22
+
23
+ # Create an issue
24
+ result = client.query(
25
+ "mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }",
26
+ { input: { teamId: "TEAM_ID", title: "My issue", description: "Description here" } }
27
+ )
28
+
29
+ # Fetch a specific issue
30
+ result = client.query(
31
+ "query($id: String!) { issue(id: $id) { id identifier title description state { name } assignee { name } url } }",
32
+ { id: "ISSUE_ID" }
33
+ )
34
+ ```
35
+
36
+ The client is a thin wrapper over Faraday that sends GraphQL queries to `https://api.linear.app/graphql`. Wrapped in a proxy that converts `Faraday::UnauthorizedError` (HTTP 401) into `Ask::Auth::InvalidCredential` with actionable error messages.
37
+
38
+ ### Authentication
39
+
40
+ The client resolves a Linear API key via `Ask::Auth.resolve(:linear_api_key)`. API keys can be provided through any configured auth provider:
41
+
42
+ 1. **Environment variable:** `LINEAR_API_KEY`
43
+ 2. **Credentials file:** `~/.ask/credentials.yml`
44
+ 3. **Rails credentials:** `Rails.application.credentials.linear_api_key`
45
+ 4. **OAuth / Database:** Custom providers
46
+
47
+ Generate an API key at [linear.app/settings/api](https://linear.app/settings/api).
48
+
49
+ ## Context Constants
50
+
51
+ Use these constants to build system prompts for AI agents:
52
+
53
+ | Constant | Value |
54
+ |---|---|
55
+ | `Ask::Linear::DESCRIPTION` | "Linear — issue tracking, project management, roadmaps, sprints" |
56
+ | `Ask::Linear::DOCS_URL` | https://developers.linear.app/docs |
57
+ | `Ask::Linear::GRAPHQL_URL` | https://api.linear.app/graphql |
58
+ | `Ask::Linear::AUTH_NAME` | `:linear_api_key` |
59
+ | `Ask::Linear::AUTH_HOW` | "https://linear.app/settings/api — generate a personal API key" |
60
+ | `Ask::Linear::GEM_NAME` | `"faraday"` |
61
+ | `Ask::Linear::QUICK_START` | Copy-paste Ruby code snippet with common GraphQL operations |
62
+
63
+ ## Error Guide
64
+
65
+ `Ask::Linear::Errors` provides structured knowledge for agents:
66
+
67
+ ```ruby
68
+ # Look up GraphQL error extension codes
69
+ Ask::Linear::Errors.for("AUTHENTICATION_ERROR")
70
+ # => { message: "...", action: "..." }
71
+
72
+ # Describe HTTP status codes
73
+ Ask::Linear::Errors.status_code_description(401)
74
+ # => "Unauthorized — API key is missing, invalid, or revoked."
75
+
76
+ # Rate limit info
77
+ Ask::Linear::Errors::RATE_LIMIT[:authenticated]
78
+ # => "100 requests per minute per API key"
79
+
80
+ # Pagination guidance
81
+ Ask::Linear::Errors::PAGINATION[:cursor_based]
82
+ # => "Linear uses cursor-based pagination with first/after or last/before arguments."
83
+ ```
84
+
85
+ ### Supported Error Codes
86
+
87
+ | Extension Code | When It Occurs |
88
+ |---|---|
89
+ | `AUTHENTICATION_ERROR` | Missing, invalid, or revoked API key |
90
+ | `FORBIDDEN` | API key lacks permission for the resource |
91
+ | `NOT_FOUND` | Resource doesn't exist or is inaccessible |
92
+ | `RATE_LIMITED` | API rate limit exceeded (100 req/min/key) |
93
+ | `INPUT_VALIDATION_ERROR` | Input data fails validation |
94
+ | `DUPLICATE_INPUT` | Resource with same data already exists |
95
+ | `INTERNAL_ERROR` | Linear server error |
96
+ | `USER_SUSPENDED` | Authenticated user account is suspended |
97
+ | `WORKSPACE_SUSPENDED` | Workspace is suspended or deactivated |
98
+
99
+ ## Client API
100
+
101
+ ### `Ask::Linear.client`
102
+
103
+ Returns an authenticated `Ask::Linear::Client` wrapped in a `ClientProxy` that catches 401 errors.
104
+
105
+ ### `client.query(gql, variables = {})`
106
+
107
+ Executes a GraphQL query or mutation against the Linear API.
108
+
109
+ **Arguments:**
110
+ - `gql` (String) — The GraphQL query or mutation string
111
+ - `variables` (Hash) — Variables to interpolate into the query (optional)
112
+
113
+ **Returns:** Hash with `"data"` key containing the response
114
+
115
+ **Raises:**
116
+ - `Ask::Auth::MissingCredential` if no API key is configured
117
+ - `Ask::Auth::InvalidCredential` if the API key returns 401
118
+ - `RuntimeError` if the API returns GraphQL errors
119
+
120
+ ### Example: Common Operations
121
+
122
+ ```ruby
123
+ client = Ask::Linear.client
124
+
125
+ # List teams
126
+ teams = client.query("query { teams { nodes { id key name } } }")
127
+
128
+ # List my assigned issues
129
+ my_issues = client.query("query { viewer { assignedIssues { nodes { id identifier title state { name } } } } }")
130
+
131
+ # Get workflow states for a team
132
+ states = client.query("query($teamId: String!) { team(id: $teamId) { states { nodes { id name type } } } }",
133
+ teamId: "TEAM_ID")
134
+
135
+ # Update issue state
136
+ result = client.query(
137
+ "mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { id identifier state { name } } } }",
138
+ { id: "ISSUE_ID", input: { stateId: "STATE_ID" } }
139
+ )
140
+
141
+ # Add comment
142
+ result = client.query(
143
+ "mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id body } } }",
144
+ { input: { issueId: "ISSUE_ID", body: "Working on this" } }
145
+ )
146
+ ```
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ bundle install
152
+ bundle exec rake test
153
+ ```
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "ask/auth"
5
+
6
+ module Ask
7
+ module Linear
8
+ # Returns an authenticated GraphQL client for the Linear API.
9
+ #
10
+ # Resolves the API key via +Ask::Auth.resolve(:linear_api_key)+ and
11
+ # returns a +Client+ that wraps Faraday and sends GraphQL queries to
12
+ # +https://api.linear.app/graphql+.
13
+ #
14
+ # Configuration:
15
+ # - +read_timeout+: +30+ seconds
16
+ # - +open_timeout+: +10+ seconds
17
+ #
18
+ # @example
19
+ # client = Ask::Linear.client
20
+ # result = client.query("query { teams { nodes { id name } } }")
21
+ #
22
+ # @return [Ask::Linear::Client] an authenticated GraphQL client
23
+ # @raise [Ask::Auth::MissingCredential] if no Linear API key is configured
24
+ # @raise [Ask::Auth::InvalidCredential] if the API key is rejected (401)
25
+ # @raise [ArgumentError] if query arguments are invalid
26
+ # @raise [RuntimeError] if Linear returns GraphQL error messages
27
+ # @raise [Faraday::Error] if the HTTP request fails
28
+ def self.client
29
+ api_key = Ask::Auth.resolve(:linear_api_key)
30
+ ClientProxy.new(Client.new(api_key))
31
+ end
32
+
33
+ # A lightweight GraphQL client for the Linear API.
34
+ class Client
35
+ # Base URL for the Linear GraphQL API.
36
+ BASE_URL = "https://api.linear.app/graphql"
37
+
38
+ # @return [Faraday::Connection] the underlying Faraday connection
39
+ attr_reader :connection
40
+
41
+ # @param api_key [String] Linear personal API key
42
+ def initialize(api_key)
43
+ @api_key = api_key
44
+ @connection = Faraday.new(url: BASE_URL) do |f|
45
+ f.request :json
46
+ f.response :json
47
+ f.response :raise_error
48
+ f.options.read_timeout = 30
49
+ f.options.open_timeout = 10
50
+ f.adapter Faraday.default_adapter
51
+ end
52
+ end
53
+
54
+ # Execute a GraphQL query or mutation against the Linear API.
55
+ #
56
+ # @param gql [String] GraphQL query or mutation string
57
+ # @param variables [Hash] Variables to interpolate into the query (default: {})
58
+ # @return [Hash] Parsed response body from the Linear API
59
+ # @raise [ArgumentError] if +gql+ is not a String or +variables+ is not a Hash
60
+ # @raise [RuntimeError] if the API returns errors in the response body
61
+ # @raise [Faraday::Error] if the HTTP request fails
62
+ def query(gql, variables = {})
63
+ unless gql.is_a?(::String)
64
+ raise ::ArgumentError, "gql must be a String, got #{gql.class}"
65
+ end
66
+
67
+ unless variables.is_a?(::Hash)
68
+ raise ::ArgumentError, "variables must be a Hash, got #{variables.class}"
69
+ end
70
+
71
+ response = @connection.post do |req|
72
+ req.headers["Authorization"] = @api_key
73
+ req.body = { query: gql, variables: variables }
74
+ end
75
+
76
+ body = response.body
77
+
78
+ if body.is_a?(Hash) && body["errors"]
79
+ messages = body["errors"].map { |e| e["message"] }.join("; ")
80
+ raise ::RuntimeError, "Linear API error: #{messages}"
81
+ end
82
+
83
+ body
84
+ end
85
+ end
86
+
87
+ # Proxies method calls to a +Client+, converting authentication
88
+ # errors into +Ask::Auth::InvalidCredential+.
89
+ class ClientProxy < BasicObject
90
+ def initialize(client)
91
+ @client = client
92
+ end
93
+
94
+ def method_missing(name, ...)
95
+ @client.public_send(name, ...)
96
+ rescue ::Faraday::UnauthorizedError => e
97
+ response = e.response
98
+ status = response.is_a?(::Hash) ? response[:status] : nil
99
+ if status == 401
100
+ ::Kernel.raise ::Ask::Auth::InvalidCredential, :linear_api_key
101
+ end
102
+ ::Kernel.raise
103
+ rescue ::Faraday::ClientError => e
104
+ response = e.response
105
+ status = response.is_a?(::Hash) ? response[:status] : nil
106
+ if status == 401
107
+ ::Kernel.raise ::Ask::Auth::InvalidCredential, :linear_api_key
108
+ end
109
+ ::Kernel.raise
110
+ end
111
+
112
+ def respond_to_missing?(name, include_private = false)
113
+ @client.respond_to?(name, include_private) || super
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Linear
5
+ # Human-readable description of the Linear service context.
6
+ DESCRIPTION = "Linear — issue tracking, project management, roadmaps, sprints"
7
+
8
+ # Base URL for Linear API documentation.
9
+ DOCS_URL = "https://developers.linear.app/docs"
10
+
11
+ # URL for the Linear GraphQL API schema (available via introspection).
12
+ GRAPHQL_URL = "https://api.linear.app/graphql"
13
+
14
+ # Credential name used with Ask::Auth.resolve.
15
+ AUTH_NAME = :linear_api_key
16
+
17
+ # Instructions for obtaining a Linear personal API key.
18
+ AUTH_HOW = "https://linear.app/settings/api — generate a personal API key"
19
+
20
+ # Gem name for the Linear API client.
21
+ GEM_NAME = "faraday"
22
+
23
+ # Required gem version constraint.
24
+ GEM_VERSION = "~> 2.0"
25
+
26
+ # URL for Faraday Ruby library documentation.
27
+ GEM_DOCS = "https://lostisland.github.io/faraday"
28
+
29
+ # Quick-start Ruby code snippet for agents to copy-paste.
30
+ QUICK_START = <<~RUBY
31
+ client = Ask::Linear.client
32
+ # List teams
33
+ result = client.query("query { teams { nodes { id key name } } }")
34
+ # Get issue by ID
35
+ result = client.query("query($id: String!) { issue(id: $id) { id identifier title description url } }", { id: "ISSUE_ID" })
36
+ # Create issue
37
+ result = client.query("mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }", { input: { teamId: "TEAM_ID", title: "New issue", description: "Description" } })
38
+ # List issues for a team
39
+ result = client.query("query { team(id: \\"TEAM_ID\\") { issues { nodes { id identifier title state { name } priority } } } }")
40
+ RUBY
41
+ end
42
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Linear
5
+ # Structured error knowledge for AI agents working with the Linear API.
6
+ #
7
+ # Provides human-readable guidance for common HTTP status codes, rate
8
+ # limiting, authentication errors, and GraphQL error extensions
9
+ # encountered when using the Linear GraphQL API.
10
+ module Errors
11
+ # Rate limit information.
12
+ #
13
+ # - Authenticated requests: 100 requests per minute per API key
14
+ #
15
+ # When rate-limited, Linear returns a 429 status code. The agent
16
+ # should wait and retry.
17
+ RATE_LIMIT = {
18
+ authenticated: "100 requests per minute per API key",
19
+ error_class: "Faraday::ClientError (HTTP 429)",
20
+ action: "Wait for the rate limit window and retry. Linear uses a sliding window rate limiter."
21
+ }.freeze
22
+
23
+ # Common HTTP status codes returned by the Linear GraphQL API and how to handle them.
24
+ #
25
+ # Linear wraps most errors in GraphQL error responses (HTTP 200 with errors body),
26
+ # but some authentication and rate-limit errors come through as HTTP status codes.
27
+ STATUS_CODES = {
28
+ 200 => "OK — The request succeeded. Check the response body for GraphQL errors.",
29
+ 400 => "Bad Request — The GraphQL query is malformed or invalid variables.",
30
+ 401 => "Unauthorized — API key is missing, invalid, or revoked. Re-authenticate.",
31
+ 403 => "Forbidden — API key lacks access to the requested resource.",
32
+ 404 => "Not Found — The requested resource does not exist.",
33
+ 422 => "Unprocessable Entity — Validation failed. Check request parameters.",
34
+ 429 => "Too Many Requests — Rate limit exceeded. Wait before retrying.",
35
+ 500 => "Internal Server Error — Linear server issue. Retry with backoff.",
36
+ 503 => "Service Unavailable — Linear is temporarily unavailable. Retry later."
37
+ }.freeze
38
+
39
+ # Pagination guidance for the Linear GraphQL API.
40
+ PAGINATION = {
41
+ cursor_based: "Linear uses cursor-based pagination with first/after or last/before arguments.",
42
+ page_size: "Use the 'first' argument to control page size (default: 50, max: 100).",
43
+ nodes: "List fields return a connection with 'nodes', 'pageInfo', and 'edges'.",
44
+ page_info: "Check pageInfo.hasNextPage and pageInfo.hasPreviousPage for more pages.",
45
+ example: 'query { issues(first: 50, after: "cursor") { nodes { id title } pageInfo { hasNextPage endCursor } } }'
46
+ }.freeze
47
+
48
+ # Map of common GraphQL error messages to human-readable guidance.
49
+ #
50
+ # Linear returns errors in the GraphQL format: +{ "errors": [{ "message": "...", "extensions": { ... } }] }+
51
+ ERRORS = {
52
+ "AUTHENTICATION_ERROR" => {
53
+ message: "The API key is missing, invalid, or has been revoked.",
54
+ action: "Generate a new API key at https://linear.app/settings/api and update your credentials."
55
+ },
56
+ "FORBIDDEN" => {
57
+ message: "Your API key does not have permission to access this resource.",
58
+ action: "Check that your API key has the necessary scopes. You may need an admin to grant access."
59
+ },
60
+ "NOT_FOUND" => {
61
+ message: "The requested resource could not be found.",
62
+ action: "Verify the ID or identifier is correct. Resources may have been deleted or you may not have access."
63
+ },
64
+ "RATE_LIMITED" => {
65
+ message: "API rate limit exceeded.",
66
+ action: "Wait before retrying. Linear allows 100 requests per minute per API key."
67
+ },
68
+ "INPUT_VALIDATION_ERROR" => {
69
+ message: "The input data failed validation.",
70
+ action: "Check required fields, data types, and constraints. Linear returns detailed error messages."
71
+ },
72
+ "DUPLICATE_INPUT" => {
73
+ message: "A resource with the same input data already exists.",
74
+ action: "Check for existing resources before creating new ones with duplicate fields."
75
+ },
76
+ "INTERNAL_ERROR" => {
77
+ message: "Linear encountered an internal server error.",
78
+ action: "Retry with exponential backoff. If the issue persists, check https://linearstatus.com."
79
+ },
80
+ "USER_SUSPENDED" => {
81
+ message: "The authenticated user account has been suspended.",
82
+ action: "Contact your Linear workspace administrator to resolve the suspension."
83
+ },
84
+ "WORKSPACE_SUSPENDED" => {
85
+ message: "The Linear workspace has been suspended or deactivated.",
86
+ action: "Contact your Linear workspace administrator or check billing status."
87
+ }
88
+ }.freeze
89
+
90
+ # Look up guidance for a GraphQL error extension code.
91
+ #
92
+ # @param error_code [String] The error extension code (e.g., "AUTHENTICATION_ERROR")
93
+ # @return [Hash, nil] A hash with +:message+ and +:action+ keys, or nil if unknown
94
+ def self.for(error_code)
95
+ ERRORS[error_code]
96
+ end
97
+
98
+ # Describe an HTTP status code.
99
+ #
100
+ # @param code [Integer] HTTP status code
101
+ # @return [String, nil] Description of the status code
102
+ def self.status_code_description(code)
103
+ STATUS_CODES[code]
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Linear
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
data/lib/ask-linear.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/linear/version"
4
+ require_relative "ask/linear/context"
5
+ require_relative "ask/linear/client"
6
+ require_relative "ask/linear/error_guide"
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-linear
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: faraday
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.25'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.25'
54
+ - !ruby/object:Gem::Dependency
55
+ name: mocha
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.1'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ description: Provides authenticated GraphQL client, context metadata, and error guide
83
+ for AI agents working with the Linear API.
84
+ email:
85
+ - kaka@myrrlabs.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - lib/ask-linear.rb
93
+ - lib/ask/linear/client.rb
94
+ - lib/ask/linear/context.rb
95
+ - lib/ask/linear/error_guide.rb
96
+ - lib/ask/linear/version.rb
97
+ homepage: https://github.com/ask-rb/ask-linear
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '3.2'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 4.0.3
116
+ specification_version: 4
117
+ summary: Linear service context for the ask-rb ecosystem
118
+ test_files: []