clai 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +19 -0
- data/LICENSE +21 -0
- data/README.md +90 -0
- data/Rakefile +12 -0
- data/docs/chat.md +10 -0
- data/docs/intro.md +10 -0
- data/docs/personas.md +23 -0
- data/docs/session.md +3 -0
- data/docs/setup.md +3 -0
- data/exe/clai +7 -0
- data/lib/clai/cli.rb +42 -0
- data/lib/clai/commands/chat.rb +13 -0
- data/lib/clai/commands/session.rb +19 -0
- data/lib/clai/commands/setup.rb +49 -0
- data/lib/clai/config.rb +18 -0
- data/lib/clai/http_client.rb +48 -0
- data/lib/clai/version.rb +5 -0
- data/lib/clai.rb +34 -0
- data/lib/strings/strings.yml +7 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4e8a4ba69b4bb0403208ae9d6a6d45b90668447ac35606761c546dae4e51451a
|
4
|
+
data.tar.gz: e3bc9eb1ffdb659059ce249cf88334abef0456f504945b3ddb243116bb756374
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c3a31a975f7a3b0a623ae19d895a5ede3ff3156b562f10367d2e6597ecdad1b3713b29874e35b292ac4c6ad8df54494b526486083280da2753bdbd6f9d66b39d
|
7
|
+
data.tar.gz: 4a14d13fda0e91c52c27f3c1cd1cd9b1e6501ce142d397310b650c8277c9f9d6cacb152f6f007e45a23a456b66c32ec1871ee721524c5805ed3e44b400d94930
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
The following topics are missing for a 1.0.0 release:
|
4
|
+
|
5
|
+
```
|
6
|
+
* Allow adding "personas" to the config ([see here](docs/personas.md))
|
7
|
+
* Rewrite with tests as soon as the basic prototype is useful
|
8
|
+
* Add GitHub Actions for PRs
|
9
|
+
* Build a landing page
|
10
|
+
```
|
11
|
+
|
12
|
+
Please use `clai` and contribute here on GitHub. I'm open to all suggestions and improvements.
|
13
|
+
|
14
|
+
0.x.x versions don't need tests or extensive documentation. Adding a small description will help me understand your change though :)
|
15
|
+
|
16
|
+
Please open a PR with a short description of your use-case and I will review and merge.
|
17
|
+
|
18
|
+
Thank you,
|
19
|
+
Niklas
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Niklas Häusele
|
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,90 @@
|
|
1
|
+
# CLAI - CLI + AI in your terminal
|
2
|
+
|
3
|
+
[![MIT](https://img.shields.io/badge/license-mit-green?style=for-the-badge)](LICENSE)
|
4
|
+
![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=for-the-badge&logo=ruby&logoColor=white)
|
5
|
+
![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
|
6
|
+
|
7
|
+
![Demo](demo.gif)
|
8
|
+
|
9
|
+
CLAI (CLI + AI = CLAI) allows you to interact with the OpenAI API in your terminal.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
First, ensure that you have Ruby installed on your system. Then, you can install the gem by running:
|
14
|
+
|
15
|
+
```
|
16
|
+
$ gem install clai
|
17
|
+
```
|
18
|
+
|
19
|
+
## Configuration
|
20
|
+
|
21
|
+
Before using the CLI tool, you need to configure it with your OpenAI API key. You can obtain an API key from the OpenAI website. Once you have your API key, run the following command and provide your key when prompted:
|
22
|
+
|
23
|
+
```
|
24
|
+
$ clai setup
|
25
|
+
```
|
26
|
+
|
27
|
+
This will create a configuration file (`~/.config/clai/clai.yml`) on your system.
|
28
|
+
|
29
|
+
Read more about the setup command [here](docs/setup.md).
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
You can chat with ChatGPT with:
|
34
|
+
|
35
|
+
```
|
36
|
+
$ clai chat "Tell me a funny joke"
|
37
|
+
```
|
38
|
+
|
39
|
+
This will send the given prompt to the API and display the generated completion.
|
40
|
+
|
41
|
+
### Help
|
42
|
+
|
43
|
+
To view the available commands and options, you can use the `help` command:
|
44
|
+
|
45
|
+
```
|
46
|
+
$ clai help
|
47
|
+
```
|
48
|
+
|
49
|
+
This will display a list of supported commands and their descriptions.
|
50
|
+
|
51
|
+
### Starting a session
|
52
|
+
|
53
|
+
You can start an interactive session with `clai session` command.
|
54
|
+
|
55
|
+
```
|
56
|
+
$ clai session
|
57
|
+
Your prompt: <Enter your prompt here><Enter>
|
58
|
+
```
|
59
|
+
|
60
|
+
This will start a REPL like process to interact with the OpenAI api.
|
61
|
+
|
62
|
+
## Examples
|
63
|
+
|
64
|
+
Here are a few examples to get you started:
|
65
|
+
|
66
|
+
```sh
|
67
|
+
# Setup clai on your machine
|
68
|
+
$ clai setup
|
69
|
+
|
70
|
+
# Start an interactive chat session
|
71
|
+
$ clai session
|
72
|
+
|
73
|
+
# Get help about the available commands and options
|
74
|
+
$ clai help
|
75
|
+
|
76
|
+
# Send a single prompt
|
77
|
+
$ clai chat "Hello World"
|
78
|
+
```
|
79
|
+
|
80
|
+
## Contributing
|
81
|
+
|
82
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
|
83
|
+
|
84
|
+
## Disclaimer
|
85
|
+
|
86
|
+
This is alpha software. Make sure to set a billing limit for your api key or unexpected costs will occur.
|
87
|
+
|
88
|
+
## License
|
89
|
+
|
90
|
+
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/docs/chat.md
ADDED
data/docs/intro.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
You can think of `clai` as ChatGPT for the terminal.
|
4
|
+
It's very basic at the moment. You can think of it as ChatGPT for the terminal. I'm currently trying to incorporate clai in my daily work, so I'm still experimenting with the API.
|
5
|
+
|
6
|
+
`clai` currently supports OpenAI and needs a paid API key to work.
|
7
|
+
|
8
|
+
My main use case currently is to have a terminal opened with a clai session and ask questions instead of googling.
|
9
|
+
|
10
|
+
0.x.0 will be unstable and untested, I'm currently aiming to release a tested 1.0.0 soon. Please see [here](https://github.com/codergeek121/clai/CONTRIBUTING.MD) on how to contribute at this stage of the project.
|
data/docs/personas.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Personas
|
2
|
+
|
3
|
+
TODO: This feature is still under development!
|
4
|
+
|
5
|
+
A clai setup should allow preconfiguring multiple personas. Each command should support a `--persona` option to allow setting some default prompts to the api.
|
6
|
+
|
7
|
+
An example could look like this:
|
8
|
+
|
9
|
+
```yaml
|
10
|
+
personas:
|
11
|
+
work:
|
12
|
+
- Use professional language
|
13
|
+
- Keep your answer below 100 words
|
14
|
+
bavarian:
|
15
|
+
- Use a bavarian accent
|
16
|
+
|
17
|
+
# Starting a session with a preselected persona
|
18
|
+
$ clai session --persona bavarian
|
19
|
+
Your prompt: <Your prompt><Enter>
|
20
|
+
Some bavarian answer.
|
21
|
+
```
|
22
|
+
|
23
|
+
The default setup should have useful personas as a default.
|
data/docs/session.md
ADDED
data/docs/setup.md
ADDED
data/exe/clai
ADDED
data/lib/clai/cli.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module CLAI
|
2
|
+
class CLI < Thor
|
3
|
+
default_task :session
|
4
|
+
|
5
|
+
desc "session", "Start an interactive chat session"
|
6
|
+
long_desc <<~DESC
|
7
|
+
Starts a REPL like chat process
|
8
|
+
|
9
|
+
> $ clai session
|
10
|
+
DESC
|
11
|
+
def session
|
12
|
+
Commands::Session.new(config).start_session
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "chat", "Prompt on the terminal"
|
16
|
+
long_desc <<~DESC
|
17
|
+
Send a single prompt to AI
|
18
|
+
|
19
|
+
> $ clai chat "Tell me a short story!"
|
20
|
+
DESC
|
21
|
+
def chat(prompt)
|
22
|
+
Commands::Chat.new(config).chat(prompt)
|
23
|
+
end
|
24
|
+
|
25
|
+
option :force, type: :boolean, default: false
|
26
|
+
desc "setup", "Setup clai on your machine"
|
27
|
+
long_desc <<~DESC
|
28
|
+
Setup `clai` on your machine. This will ask you for a api key and create a base config file.
|
29
|
+
|
30
|
+
> $ clai setup
|
31
|
+
DESC
|
32
|
+
def setup
|
33
|
+
Commands::Setup.new.run(options[:force])
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def config
|
39
|
+
@config ||= Config.parse
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CLAI
|
2
|
+
module Commands
|
3
|
+
class Session
|
4
|
+
def initialize(config)
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_session
|
9
|
+
loop do
|
10
|
+
print S.(:prompt)
|
11
|
+
question = $stdin.gets
|
12
|
+
break unless question
|
13
|
+
HTTPClient.new(@config).chat(question.strip)
|
14
|
+
puts
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CLAI
|
2
|
+
module Commands
|
3
|
+
class Setup
|
4
|
+
def run(force)
|
5
|
+
if clai_setup? && !force
|
6
|
+
puts S.(:already_setup)
|
7
|
+
else
|
8
|
+
generate_config_directory
|
9
|
+
ask_for_open_ai_api_key
|
10
|
+
generate_sample_config_file
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def clai_setup?
|
17
|
+
File.exist?(File.expand_path("~/.config/clai/clai.yml"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask_for_open_ai_api_key
|
21
|
+
puts S.(:disclaimer)
|
22
|
+
print S.(:paste_key)
|
23
|
+
input = $stdin.gets.strip
|
24
|
+
if input.length > 0
|
25
|
+
@api_key = input
|
26
|
+
else
|
27
|
+
@api_key = "<No API key specified>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_config_directory
|
32
|
+
FileUtils.mkdir_p(File.expand_path("~/.config/clai"))
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_sample_config_file
|
36
|
+
File.write(CLAI.config_file_path, sample_config)
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: use erubis and move to /strings and rename /strings
|
40
|
+
def sample_config
|
41
|
+
<<~YML
|
42
|
+
# Read more about the clai.yml config file at https://github.com/codergeek121/clai/docs/setup.md
|
43
|
+
api_key: #{@api_key}
|
44
|
+
model: gpt-3.5-turbo
|
45
|
+
YML
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/clai/config.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module CLAI
|
2
|
+
class Config
|
3
|
+
def self.parse
|
4
|
+
config = YAML.safe_load_file(CLAI.config_file_path)
|
5
|
+
new(api_key: config["api_key"], model: config["model"])
|
6
|
+
rescue Errno::ENOENT
|
7
|
+
puts S.(:no_clai_yml)
|
8
|
+
exit(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :api_key, :model
|
12
|
+
|
13
|
+
def initialize(api_key:, model:)
|
14
|
+
@api_key = api_key
|
15
|
+
@model = model
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module CLAI
|
2
|
+
class HTTPClient
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def chat(query)
|
8
|
+
puts
|
9
|
+
client
|
10
|
+
.post("https://api.openai.com/v1/chat/completions",
|
11
|
+
json: {
|
12
|
+
model: @config.model,
|
13
|
+
messages: [
|
14
|
+
{ "role": "user", "content": query }
|
15
|
+
],
|
16
|
+
stream: true
|
17
|
+
}
|
18
|
+
).body.each_with_index do |chunk, i|
|
19
|
+
if i == 0
|
20
|
+
# TODO: remove workaround
|
21
|
+
splits = chunk.split("\n\n")
|
22
|
+
if splits.length > 1
|
23
|
+
chunk = splits[1]
|
24
|
+
else
|
25
|
+
chunk = splits[0]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
data = chunk.match(/data: (.*)/) || []
|
29
|
+
|
30
|
+
break if data[1] == "[DONE]"
|
31
|
+
|
32
|
+
if data[1]
|
33
|
+
result = JSON.parse(data[1])
|
34
|
+
print result.dig('choices', 0, "delta", "content")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def client
|
43
|
+
HTTP
|
44
|
+
.auth("Bearer #{@config.api_key}")
|
45
|
+
.headers(accept: "application/json")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/clai/version.rb
ADDED
data/lib/clai.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "http"
|
5
|
+
require "zeitwerk"
|
6
|
+
require "debug"
|
7
|
+
require "fileutils"
|
8
|
+
require "yaml"
|
9
|
+
|
10
|
+
loader = Zeitwerk::Loader.for_gem
|
11
|
+
loader.inflector.inflect(
|
12
|
+
"clai" => "CLAI",
|
13
|
+
"http_client" => "HTTPClient",
|
14
|
+
"cli" => "CLI",
|
15
|
+
)
|
16
|
+
loader.setup
|
17
|
+
|
18
|
+
module CLAI
|
19
|
+
class Error < StandardError; end
|
20
|
+
class APIKeyMissing < Error; end
|
21
|
+
|
22
|
+
@@config_file_path = File.expand_path("~/.config/clai/clai.yml")
|
23
|
+
def self.config_file_path
|
24
|
+
@@config_file_path
|
25
|
+
end
|
26
|
+
|
27
|
+
@@strings = YAML.safe_load_file(File.expand_path("../strings/strings.yml", __FILE__))
|
28
|
+
def self.strings
|
29
|
+
@@strings
|
30
|
+
end
|
31
|
+
S = ->(name) { @@strings.fetch(name.to_s) { raise "Unknown translation! Please file a bug!" } }
|
32
|
+
end
|
33
|
+
|
34
|
+
loader.eager_load
|
@@ -0,0 +1,7 @@
|
|
1
|
+
disclaimer: This is alpha software, it's probably broken. Please specify a billing limit for your API key to avoid unexpected costs
|
2
|
+
paste_key: "Paste your OpenAI API key and press enter: "
|
3
|
+
already_setup: |
|
4
|
+
Clai is already setup! To regenerate a new config run:
|
5
|
+
$ clai setup --force
|
6
|
+
no_clai_yml: No clai.yml config file found. Please run 'clai setup'
|
7
|
+
prompt: "Your prompt: "
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Niklas Häusele
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zeitwerk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.6.8
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.6'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.6.8
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: http
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.1'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 5.1.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '5.1'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 5.1.1
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: thor
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.2'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.2.2
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.2'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.2.2
|
73
|
+
description: Your personal AI for the terminal
|
74
|
+
email:
|
75
|
+
- niklas.haeusele@hey.com
|
76
|
+
executables:
|
77
|
+
- clai
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- CONTRIBUTING.md
|
82
|
+
- LICENSE
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- docs/chat.md
|
86
|
+
- docs/intro.md
|
87
|
+
- docs/personas.md
|
88
|
+
- docs/session.md
|
89
|
+
- docs/setup.md
|
90
|
+
- exe/clai
|
91
|
+
- lib/clai.rb
|
92
|
+
- lib/clai/cli.rb
|
93
|
+
- lib/clai/commands/chat.rb
|
94
|
+
- lib/clai/commands/session.rb
|
95
|
+
- lib/clai/commands/setup.rb
|
96
|
+
- lib/clai/config.rb
|
97
|
+
- lib/clai/http_client.rb
|
98
|
+
- lib/clai/version.rb
|
99
|
+
- lib/strings/strings.yml
|
100
|
+
homepage: https://github.com/codergeek121/clai
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
homepage_uri: https://github.com/codergeek121/clai
|
105
|
+
source_code_uri: https://github.com/codergeek121/clai
|
106
|
+
post_install_message: " _____ _ _____ \n / ____| | / |_ _|\n|
|
107
|
+
| | | / | | \n| | | | / / | | \n| |____| |____ / ____
|
108
|
+
\ _| |_ \n _____|______/_/ ______|\n \nThank you for
|
109
|
+
using clai.\n\nclai is early alpha software, beware of unexpected costs.\nMake sure
|
110
|
+
to add a billing limit to your OpenAI api key.\n\nTo start using clai, run 'clai
|
111
|
+
setup'\n"
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: 2.6.0
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubygems_version: 3.4.16
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Your personal AI for the terminal
|
130
|
+
test_files: []
|