ask-anychat 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: 94d004dde4b043c13876327f103c7c35e21cfd5027aace5cb8df0f3b8c97248a
4
+ data.tar.gz: b7abca34b3ca86d763fe170bd13ea469693a7dd62e135b59b068e2ab9aa8f7b6
5
+ SHA512:
6
+ metadata.gz: c5d6e51b5b420cdfedf8b8f1feed43fbd3ffa478d3a4783d16ad8ca80b9205d0d070d172342344d2997fc7cbe1369c811bcb1fd80dee37dc9f74a4b4713bab03
7
+ data.tar.gz: dfc675d8e95290db9cfa45a6e0fefc80ea1df01197a3349e838428006c0ab24864b8fb89a9bc7a87081c37f673ce5b65df1aa6925f22c671ca35df79381a7cb4
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ ## [0.1.0] - 2026-09-17
2
+
3
+ ### Added
4
+
5
+ - **The Anychat API client** — `Ask::AnyChat.client` resolves its base URL from
6
+ the hosted app and its token through ask-auth (`ANYCHAT_TOKEN`, then the
7
+ credentials file, then Rails credentials, then the database), so most callers
8
+ pass neither.
9
+
10
+ - **Workspace agent CRUD** — `workspace_agents`, `workspace_agent`,
11
+ `create_workspace_agent`, `update_workspace_agent` and
12
+ `destroy_workspace_agent`, addressing an agent by the address it answers on.
13
+ Creating takes a name and derives the address from it, handing a taken one the
14
+ first free variant rather than failing.
15
+
16
+ - **Errors under one parent** — `Ask::AnyChat::Error` with `Unauthorized`,
17
+ `NotFound`, `Invalid`, `Api` and `Unreachable` beneath it, each carrying the
18
+ sentence the API answered with. A workspace the token cannot see is reported
19
+ as not found rather than forbidden, which is the API's answer as well.
20
+
21
+ - **A bundled skill** — `anychat.use_anychat`, so a model that has the gem also
22
+ has the instructions for using it.
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,63 @@
1
+ # Ask::AnyChat
2
+
3
+ Anychat API client for the [ask-rb](https://github.com/ask-rb) ecosystem.
4
+
5
+ Anychat gives a business an agent of its own — a page customers can visit, a chat
6
+ they can talk to, and the words it answers with. This gem creates and manages the
7
+ agents a workspace owns.
8
+
9
+ This is the client layer only. Tool framing — MCP servers, native `Ask::Tools` —
10
+ is provided by the consumers.
11
+
12
+ ## Installation
13
+
14
+ ```ruby
15
+ gem "ask-anychat"
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ client = Ask::AnyChat.client
22
+
23
+ client.workspace_agents("anywaye") # every agent a workspace owns
24
+ client.workspace_agent("anywaye", "support") # one, by the address it answers on
25
+ client.create_workspace_agent("anywaye", display_name: "Support")
26
+ client.update_workspace_agent("anywaye", "support", display_name: "Help desk")
27
+ client.destroy_workspace_agent("anywaye", "support")
28
+ ```
29
+
30
+ The base URL defaults to the hosted app and the token is resolved through
31
+ [ask-auth](https://github.com/ask-rb/ask-auth) — `ANYCHAT_TOKEN` in the
32
+ environment, then the credentials file, then Rails credentials, then the
33
+ database. Mint a token in Anychat under Settings → API tokens.
34
+
35
+ Point it somewhere else with `Ask::AnyChat.client(base_url:, token:)`.
36
+
37
+ An agent in Anychat is a **workspace agent**: the system templates every agent is
38
+ cloned from are not what this reaches, only the agents a workspace owns. An
39
+ address that belongs to a workspace the token cannot see is reported as not found
40
+ rather than forbidden — the same answer as one that does not exist.
41
+
42
+ ### Failures
43
+
44
+ Everything raises under `Ask::AnyChat::Error`, carrying the sentence the API
45
+ answered with.
46
+
47
+ | Raised | What it means |
48
+ | --- | --- |
49
+ | `Error::Unauthorized` | The token is missing, expired or refused |
50
+ | `Error::NotFound` | No such workspace, or no such agent in it |
51
+ | `Error::Invalid` | The request was understood and refused — the message says why |
52
+ | `Error::Api` | The API failed, or answered with something unexpected |
53
+ | `Error::Unreachable` | The API could not be reached at all |
54
+
55
+ A refusal is not worth retrying; `Error::Unreachable` and `Error::Api` are.
56
+
57
+ ## Contributing
58
+
59
+ Run `bin/setup` once, then `bundle exec rake test`.
60
+
61
+ ## License
62
+
63
+ MIT.
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "faraday"
5
+ require "faraday/retry"
6
+ require "ask/auth"
7
+
8
+ module Ask
9
+ module AnyChat
10
+ # A client for the Anychat API.
11
+ #
12
+ # Every call names a workspace, because a workspace is what a token reaches:
13
+ # the API resolves it through the token's user, so a token sees only the
14
+ # workspaces its user belongs to. Naming somebody else's workspace is the
15
+ # same answer as naming one that does not exist, which is the point.
16
+ #
17
+ # client = Ask::AnyChat.client(base_url: "https://anywaye.com", token: "...")
18
+ # client.workspace_agents("anywaye")
19
+ #
20
+ # Failures raise under Error, carrying the sentence the API answered with.
21
+ class Client
22
+ RETRY_STATUSES = [429, 500, 502, 503].freeze
23
+ OPEN_TIMEOUT = 10
24
+
25
+ attr_reader :base_url
26
+
27
+ def initialize(base_url:, token:, timeout: 30)
28
+ @base_url = normalize(base_url)
29
+ @token = token
30
+ @timeout = timeout
31
+ end
32
+
33
+ # The agents a workspace owns, oldest first.
34
+ def workspace_agents(workspace)
35
+ fetch_key(get(agents_path(workspace)), "agents")
36
+ end
37
+
38
+ # One agent, by the address it answers on.
39
+ def workspace_agent(workspace, handle)
40
+ fetch_key(get("#{agents_path(workspace)}/#{encode(handle)}"), "agent")
41
+ end
42
+
43
+ def create_workspace_agent(workspace, **attributes)
44
+ fetch_key(post(agents_path(workspace), agent: attributes), "agent")
45
+ end
46
+
47
+ def update_workspace_agent(workspace, handle, **attributes)
48
+ fetch_key(patch("#{agents_path(workspace)}/#{encode(handle)}", agent: attributes), "agent")
49
+ end
50
+
51
+ # An address the owner printed is the reason they care about this one, so
52
+ # it is gone afterwards and free for the next agent to take. Nothing comes
53
+ # back but the fact that it worked; a failure raises.
54
+ def destroy_workspace_agent(workspace, handle)
55
+ delete("#{agents_path(workspace)}/#{encode(handle)}")
56
+ end
57
+
58
+ # The headers every request carries. Public because it is the honest
59
+ # answer to "what does this client send?" — and because a caller swapping
60
+ # the connection under it should be able to keep them.
61
+ def default_headers
62
+ {
63
+ "Authorization" => "Bearer #{token}",
64
+ "Accept" => "application/json",
65
+ "User-Agent" => "ask-anychat/#{VERSION}"
66
+ }
67
+ end
68
+
69
+ private
70
+
71
+ attr_reader :token, :timeout
72
+
73
+ def agents_path(workspace)
74
+ "/api/#{API_VERSION}/workspaces/#{encode(workspace)}/agents"
75
+ end
76
+
77
+ def get(path, params = nil)
78
+ request(:get, path, params: params)
79
+ end
80
+
81
+ def post(path, body = nil)
82
+ request(:post, path, body: body)
83
+ end
84
+
85
+ def patch(path, body = nil)
86
+ request(:patch, path, body: body)
87
+ end
88
+
89
+ def delete(path)
90
+ request(:delete, path)
91
+ end
92
+
93
+ def request(method, path, body: nil, params: nil)
94
+ response = connection.public_send(method, path) do |request|
95
+ request.params = params if params
96
+ request.body = body if body
97
+ end
98
+
99
+ response.success? ? parse(response) : raise_for(response)
100
+ rescue Faraday::Error => e
101
+ raise Error::Unreachable, "#{base_url} could not be reached: #{e.message}"
102
+ end
103
+
104
+ def connection
105
+ @connection ||= Faraday.new(url: base_url, headers: default_headers) do |builder|
106
+ builder.request :json
107
+ builder.response :json, content_type: /\bjson$/
108
+ builder.request :retry, max: 3, retry_statuses: RETRY_STATUSES, interval: 0.5
109
+ builder.options.timeout = timeout
110
+ builder.options.open_timeout = OPEN_TIMEOUT
111
+ builder.adapter Faraday.default_adapter
112
+ end
113
+ end
114
+
115
+ # A body is an object, or nothing at all — a 204 has nothing to read. A
116
+ # body that is neither is the API answering with something this gem does
117
+ # not know what to do with, which is worth saying out loud.
118
+ def parse(response)
119
+ body = response.body
120
+ return {} if body.nil? || (body.is_a?(String) && body.strip.empty?)
121
+ return body if body.is_a?(Hash)
122
+
123
+ raise Error::Api, "the API answered with #{body.class} where an object was expected"
124
+ end
125
+
126
+ def raise_for(response)
127
+ message = message_from(response)
128
+
129
+ case response.status
130
+ when 401 then raise Error::Unauthorized, message
131
+ when 404 then raise Error::NotFound, message
132
+ when 422 then raise Error::Invalid, message
133
+ else raise Error::Api, "HTTP #{response.status} from #{base_url}: #{message}"
134
+ end
135
+ end
136
+
137
+ # A failure is answered as {"error" => "..."} and sometimes with a
138
+ # field-by-field map beside it. The sentence is what a person reads, so it
139
+ # is preferred over the map and over the status code.
140
+ def message_from(response)
141
+ body = response.body
142
+ return body.to_s unless body.is_a?(Hash)
143
+
144
+ sentence = body["error"].to_s.strip
145
+ return sentence unless sentence.empty?
146
+ return body["errors"].to_s if body["errors"]
147
+
148
+ "the request was refused"
149
+ end
150
+
151
+ def fetch_key(body, key)
152
+ body.fetch(key) { raise Error::Api, "the API answered without #{key.inspect}: #{body.inspect}" }
153
+ end
154
+
155
+ def encode(segment)
156
+ ERB::Util.url_encode(segment.to_s)
157
+ end
158
+
159
+ def normalize(url)
160
+ url.to_s.sub(%r{/+\z}, "")
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module AnyChat
5
+ # What this is, for a model deciding whether to reach for it.
6
+ DESCRIPTION = "Anychat gives a business an agent of its own — a page customers " \
7
+ "can visit, a chat they can talk to, and the words it answers with. " \
8
+ "This client creates and manages those agents."
9
+
10
+ DOMAIN_URL = "https://anywaye.com"
11
+ DOCS_URL = "https://anywaye.com/docs/api"
12
+
13
+ # ask-auth resolves this name in order: ANYCHAT_TOKEN in the environment,
14
+ # the credentials file, Rails credentials, then the database.
15
+ AUTH_NAME = :anychat_token
16
+ AUTH_HOW = "Mint a token in Anychat under Settings → API tokens, then set it as " \
17
+ "ANYCHAT_TOKEN, or pass token: to Ask::AnyChat.client."
18
+
19
+ API_VERSION = "v1"
20
+
21
+ QUICK_START = <<~RUBY
22
+ client = Ask::AnyChat.client(token: ENV["ANYCHAT_TOKEN"])
23
+
24
+ client.workspace_agents("anywaye") # every agent a workspace owns
25
+ client.workspace_agent("anywaye", "support") # one, by the address it answers on
26
+ client.create_workspace_agent("anywaye", display_name: "Support")
27
+ client.update_workspace_agent("anywaye", "support", display_name: "Help desk")
28
+ client.destroy_workspace_agent("anywaye", "support")
29
+ RUBY
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module AnyChat
5
+ # Everything this gem raises, under one parent, so a caller can rescue the
6
+ # library rather than a list of its moods. Each one carries the sentence the
7
+ # API answered with: "An agent needs a name." is worth more than "422".
8
+ class Error < StandardError
9
+ # The token was missing, expired, or refused.
10
+ class Unauthorized < Error; end
11
+
12
+ # No such workspace — or none this token can reach, which is the same
13
+ # answer on purpose.
14
+ class NotFound < Error; end
15
+
16
+ # The API understood the request and refused it; the message says why.
17
+ class Invalid < Error; end
18
+
19
+ # The API failed, or answered with something this gem did not expect.
20
+ class Api < Error; end
21
+
22
+ # The API could not be reached at all.
23
+ class Unreachable < Error; end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module AnyChat
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: anychat.use_anychat
3
+ description: Manage the agents an Anychat workspace owns — list them, bring a new one into being, rename or re-title one, or retire one. Use when a request concerns an Anychat agent's name, its address, or the set of agents a workspace has.
4
+ ---
5
+
6
+ # Using Anychat
7
+
8
+ Anychat gives a business an agent of its own: a page customers can visit, a chat
9
+ they can talk to, and the words it answers with. This client creates and manages
10
+ those agents.
11
+
12
+ An agent in Anychat is a **workspace agent**. A workspace owns one or many, and
13
+ each answers on `/<workspace>/<handle>` — the link the owner prints. The system
14
+ templates every agent is cloned from are not what this reaches; only the agents a
15
+ workspace owns.
16
+
17
+ ## Step 1: get the client
18
+
19
+ ```ruby
20
+ client = Ask::AnyChat.client
21
+ ```
22
+
23
+ The base URL defaults to the hosted app and the token is resolved through
24
+ ask-auth, so this usually needs no arguments. To point somewhere else:
25
+
26
+ ```ruby
27
+ Ask::AnyChat.client(base_url: "https://staging.anywaye.com", token: ENV["STAGING_TOKEN"])
28
+ ```
29
+
30
+ ## Step 2: name the workspace
31
+
32
+ Every call names one, because a workspace is what a token reaches: the API
33
+ resolves it through the token's user, so a token sees only the workspaces its
34
+ user belongs to. Naming somebody else's workspace is the same answer as naming
35
+ one that does not exist — a `NotFound`, not a `Forbidden`.
36
+
37
+ ```ruby
38
+ client.workspace_agents("anywaye")
39
+ ```
40
+
41
+ ## Step 3: read before you write
42
+
43
+ ```ruby
44
+ client.workspace_agents("anywaye") # every agent, oldest first
45
+ client.workspace_agent("anywaye", "support") # one, by the address it answers on
46
+ ```
47
+
48
+ Read first for two reasons: it tells you which addresses are already taken, and
49
+ it gives you the `handle` that `update` and `destroy` need. There is no lookup
50
+ by name — an agent is addressed by its address.
51
+
52
+ ## Step 4: create
53
+
54
+ ```ruby
55
+ client.create_workspace_agent("anywaye", display_name: "Support")
56
+ ```
57
+
58
+ Only a name is required. The address is derived from it, and a taken one is not
59
+ a failure — the new agent is handed the first free variant (`support-2`,
60
+ `support-3`). Pass `handle:` to choose the address yourself when the owner has
61
+ one in mind.
62
+
63
+ `description:` and `public:` may be given too; everything else an agent shows
64
+ (branding, greeting, colour) is the owner's to edit afterwards.
65
+
66
+ ## Step 5: change one
67
+
68
+ ```ruby
69
+ client.update_workspace_agent("anywaye", "support", display_name: "Help desk")
70
+ client.update_workspace_agent("anywaye", "support", handle: "help")
71
+ ```
72
+
73
+ Only the attributes you pass are written. Renaming the address is safe: the
74
+ handler allocates a free one if what you asked for is taken.
75
+
76
+ ## Step 6: retire one
77
+
78
+ ```ruby
79
+ client.destroy_workspace_agent("anywaye", "support")
80
+ ```
81
+
82
+ The address goes with it and becomes free again. Conversations customers had
83
+ with it are kept — they are a record of what somebody was told — so a retired
84
+ agent still appears in that history.
85
+
86
+ ## When it is refused
87
+
88
+ Every failure raises under `Ask::AnyChat::Error`, carrying the sentence the API
89
+ answered with, which is written to be read out:
90
+
91
+ | Raised | What it means |
92
+ | --- | --- |
93
+ | `Error::Unauthorized` | The token is missing, expired or refused |
94
+ | `Error::NotFound` | No such workspace, or no such agent in it |
95
+ | `Error::Invalid` | The request was understood and refused — the message says why |
96
+ | `Error::Api` | The API failed, or answered with something unexpected |
97
+ | `Error::Unreachable` | The API could not be reached at all |
98
+
99
+ ```ruby
100
+ begin
101
+ client.create_workspace_agent("anywaye", display_name: "")
102
+ rescue Ask::AnyChat::Error::Invalid => e
103
+ e.message # => "An agent needs a name."
104
+ end
105
+ ```
106
+
107
+ A refusal is not something to retry. `Error::Unreachable` and `Error::Api` are.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/anychat/version"
4
+ require_relative "ask/anychat/context"
5
+ require_relative "ask/anychat/errors"
6
+ require_relative "ask/anychat/client"
7
+
8
+ module Ask
9
+ # Anychat: an agent for a business — a page, a chat, and the words it answers
10
+ # with — and the client that creates and manages one.
11
+ module AnyChat
12
+ # A client for the Anychat API.
13
+ #
14
+ # Ask::AnyChat.client(token: ENV["ANYCHAT_TOKEN"])
15
+ #
16
+ # The base URL defaults to the hosted app, and the token is resolved through
17
+ # ask-auth (ANYCHAT_TOKEN, then the credentials file, then Rails
18
+ # credentials, then the database), so most callers pass one of the two —
19
+ # often neither.
20
+ def self.client(base_url: nil, token: nil, **options)
21
+ Client.new(
22
+ base_url: base_url || ENV["ANYCHAT_BASE_URL"] || DOMAIN_URL,
23
+ token: token || Auth.resolve(AUTH_NAME),
24
+ **options
25
+ )
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-anychat
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.3.2
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.3.2
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: faraday-retry
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
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: rake
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '13.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '13.0'
96
+ description: 'Creates and manages the agents an Anychat workspace owns. Anychat gives
97
+ a business an agent of its own — a page customers can visit, a chat they can talk
98
+ to, and the words it answers with. This is the client layer only: the tool framing
99
+ that exposes it to an agent (MCP servers, native Ask::Tools) is provided by the
100
+ consumers.'
101
+ email:
102
+ - kaka@myrrlabs.com
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - CHANGELOG.md
108
+ - LICENSE
109
+ - README.md
110
+ - lib/ask-anychat.rb
111
+ - lib/ask/anychat/client.rb
112
+ - lib/ask/anychat/context.rb
113
+ - lib/ask/anychat/errors.rb
114
+ - lib/ask/anychat/version.rb
115
+ - lib/ask/skills/anychat.use_anychat/SKILL.md
116
+ homepage: https://github.com/ask-rb/ask-anychat
117
+ licenses:
118
+ - MIT
119
+ metadata:
120
+ homepage_uri: https://github.com/ask-rb/ask-anychat
121
+ source_code_uri: https://github.com/ask-rb/ask-anychat
122
+ changelog_uri: https://github.com/ask-rb/ask-anychat/blob/master/CHANGELOG.md
123
+ rubygems_mfa_required: 'true'
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '3.2'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubygems_version: 4.0.18
139
+ specification_version: 4
140
+ summary: Anychat API client for the ask-rb ecosystem
141
+ test_files: []