aicli 0.1.1

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: 38ddbde1455388bed7ee62cff84c0aaa5cbc7329b0210fb88aacdf9fe39a0e13
4
+ data.tar.gz: 645ca62e82b191c88951293c8b04263428c7d5c3e4dfa7e6481db6ff59771a1c
5
+ SHA512:
6
+ metadata.gz: a36e4429798ec0261b2d65b39d038e6276d7680ed87d042d8b8a20b5ede788b5ba5010303103d36c87f25b69ebdcd520ed651f4668110d0ad3d341fb6d0852bf
7
+ data.tar.gz: 68d9f6c8c3789eca4c5b9ff4e65e31d3598ab5f917985293dab89048c3651ba20ba7761fa46c06f04b06f247b0316adb785c0d0cb611955ae1db032a42f6a85c
data/CHANGELOG.md ADDED
@@ -0,0 +1,151 @@
1
+ ## 1.0.12
2
+
3
+ - Bug fixes
4
+ - Default to gpt-4o-mini instead of gpt-3.5-turbo
5
+
6
+ ## 1.0.8
7
+
8
+ - Fix a bug causing completions to not display in some cases
9
+ - Allow hitting "q" or "esc" to stop the streaming output
10
+
11
+ ## 1.0.7
12
+
13
+ - Fix a bug causing completions to not display in some cases
14
+
15
+ ## 1.0.6
16
+
17
+ - Fix for streaming data including the shell command name erroneously, causing the command to fail
18
+
19
+ ## 1.0.5
20
+
21
+ - Fix using the configured model in all cases
22
+
23
+ ## 1.0.4
24
+
25
+ - Improve `--help` menu descriptions
26
+
27
+ ## 1.0.3
28
+
29
+ - Fix choosing custom models
30
+
31
+ ## 1.0.2
32
+
33
+ - Add Turkish language support
34
+ - Improve config UX (correctly `await` on setting configs)
35
+
36
+ ## 1.0.1
37
+
38
+ - Fix a typo ("to to" -> "to") [#78](https://github.com/BuilderIO/ai-shell/pull/78)
39
+
40
+ ## 1.0.0
41
+
42
+ - Overdue major version bump to signify the stability of this CLI
43
+ - Add Indonesian language support
44
+
45
+ ## 0.1.23
46
+
47
+ - Add support for Android [#75](https://github.com/BuilderIO/ai-shell/pull/75)
48
+
49
+ ## 0.1.22
50
+
51
+ - Fix a crash on linux [#64](https://github.com/BuilderIO/ai-shell/issues/64)
52
+
53
+ ## 0.1.21
54
+
55
+ - Fix a bug preventing initial configuration [#69](https://github.com/BuilderIO/ai-shell/issues/69)
56
+
57
+ ## 0.1.20
58
+
59
+ - Multi language support [#62](https://github.com/BuilderIO/ai-shell/pull/62)
60
+
61
+ ## 0.1.19
62
+
63
+ - Support Azure keys
64
+
65
+ ## 0.1.18
66
+
67
+ - Option to copy the generated script to the clipboard
68
+
69
+ ## 0.1.15
70
+
71
+ - Fix chat mode to support chat history
72
+
73
+ ## 0.1.14
74
+
75
+ - Adds chat mode
76
+ - Fixes "ai update" to not eagerly capture
77
+ - Improve operating system identification
78
+
79
+ ## 0.1.13
80
+
81
+ - Fix for using cloudflare proxy (fix streaming logic to handle partial chunks)
82
+
83
+ ## 0.1.12
84
+
85
+ - Support custom API endpoints
86
+
87
+ ## 0.1.11
88
+
89
+ - `ai update` command
90
+ - Minor UX improvements and fixes
91
+
92
+ ## 0.1.10
93
+
94
+ - Obfuscate API key from config UI
95
+
96
+ ## 0.1.9
97
+
98
+ - Visual config UI (from `ai config ui`)
99
+
100
+ ## 0.1.8
101
+
102
+ - Better error handling (handle streams and non streams, JSON and strings)
103
+
104
+ ## 0.1.7
105
+
106
+ - Option to edit script before running
107
+ - Fix error with String.prototype.replaceAll not existing
108
+
109
+ ## 0.1.6
110
+
111
+ - Silent mode (skip explanation)
112
+
113
+ ## 0.1.5
114
+
115
+ - Improve error messaging UX, again x3
116
+
117
+ ## 0.1.4
118
+
119
+ - Improve error messaging, again again
120
+
121
+ ## 0.1.3
122
+
123
+ - Improve error messaging, again
124
+
125
+ ## 0.1.2
126
+
127
+ - Improve error messaging
128
+
129
+ ## 0.1.1
130
+
131
+ - Minor UX improvement ("AI Shell" vs "ai-shell")
132
+
133
+ ## 0.1.0
134
+
135
+ - Minor error message improvements
136
+
137
+ ## 0.0.18
138
+
139
+ - Better error handling
140
+
141
+ ## 0.0.17
142
+
143
+ - Minor improvements to the UX of the revision flow
144
+
145
+ ## 0.0.16
146
+
147
+ - Take the current operating system into account
148
+
149
+ ## 0.0.15
150
+
151
+ - Stream responses
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Antonio Molinari
4
+ Copyright (c) 2023 Builder.io (original Node.js implementation)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,176 @@
1
+ <h2 align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fb5b9997cec2c4fffb3e5c5e9bb4fed7d">
4
+ <img width="300" alt="aicli logo" src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fb7f9d2d9911a4199a9d26f8ba210b3f8">
5
+ </picture>
6
+ </h2>
7
+
8
+ <h4 align="center">
9
+ A CLI that converts natural language to shell commands.
10
+ </h4>
11
+
12
+ <p align="center">
13
+ Inspired by the <a href="https://githubnext.com/projects/copilot-cli">GitHub Copilot X CLI</a>, but open source for everyone.
14
+ </p>
15
+
16
+ <br>
17
+
18
+ # aicli
19
+
20
+ Ruby port by [Antonio Molinari](https://github.com/magnum), inspired by the original [BuilderIO/ai-shell](https://github.com/BuilderIO/ai-shell) CLI.
21
+
22
+ ## Setup
23
+
24
+ > Requires Ruby 3.0+
25
+
26
+ 1. Install _aicli_:
27
+
28
+ ```sh
29
+ gem install aicli
30
+ ```
31
+
32
+ Or from this repository:
33
+
34
+ ```sh
35
+ bundle install
36
+ bundle exec rake install
37
+ # or
38
+ gem build aicli.gemspec && gem install ./aicli-*.gem
39
+ ```
40
+
41
+ 2. Retrieve your API key from [OpenAI](https://platform.openai.com/account/api-keys)
42
+
43
+ > Note: If you haven't already, you'll have to create an account and set up billing.
44
+
45
+ 3. Set the key so aicli can use it:
46
+
47
+ ```sh
48
+ aicli config set OPENAI_KEY=<your token>
49
+ ```
50
+
51
+ This will create a `.aicli` file in your home directory.
52
+
53
+ ## Usage
54
+
55
+ ```bash
56
+ aicli <prompt>
57
+ ```
58
+
59
+ For example:
60
+
61
+ ```bash
62
+ aicli list all log files
63
+ ```
64
+
65
+ Then you will get an output where you can choose to run the suggested command, revise it via a prompt, edit it, copy it, or cancel.
66
+
67
+ The short alias `ai` is also available.
68
+
69
+ ### Special characters
70
+
71
+ Some shells handle characters like `?` or `*` specially. Wrap the prompt in quotes if needed:
72
+
73
+ ```bash
74
+ aicli 'what is my ip address'
75
+ ```
76
+
77
+ ### Chat mode
78
+
79
+ ```bash
80
+ aicli chat
81
+ ```
82
+
83
+ Engage in a conversation with the AI directly through the CLI.
84
+
85
+ ### Silent mode (skip explanations)
86
+
87
+ ```bash
88
+ aicli -s list all log files
89
+ ```
90
+
91
+ Or save the preference:
92
+
93
+ ```bash
94
+ aicli config set SILENT_MODE=true
95
+ ```
96
+
97
+ ### Custom API endpoint
98
+
99
+ ```sh
100
+ aicli config set OPENAI_API_ENDPOINT=<your proxy endpoint>
101
+ ```
102
+
103
+ Default: `https://api.openai.com/v1`
104
+
105
+ ### Set Language
106
+
107
+ | Language | Key |
108
+ | ------------------- | ------- |
109
+ | English | en |
110
+ | Simplified Chinese | zh-Hans |
111
+ | Traditional Chinese | zh-Hant |
112
+ | Spanish | es |
113
+ | Japanese | jp |
114
+ | Korean | ko |
115
+ | French | fr |
116
+ | German | de |
117
+ | Russian | ru |
118
+ | Ukrainian | uk |
119
+ | Vietnamese | vi |
120
+ | Arabic | ar |
121
+ | Portuguese | pt |
122
+ | Turkish | tr |
123
+ | Indonesian | id |
124
+ | Italian | it |
125
+
126
+ ```sh
127
+ aicli config set LANGUAGE=zh-Hans
128
+ ```
129
+
130
+ ### Config UI
131
+
132
+ ```bash
133
+ aicli config
134
+ ```
135
+
136
+ ### Upgrading
137
+
138
+ ```bash
139
+ aicli --version
140
+ gem update aicli
141
+ ```
142
+
143
+ Or:
144
+
145
+ ```bash
146
+ aicli update
147
+ ```
148
+
149
+ ## Development
150
+
151
+ ```sh
152
+ bundle install
153
+ bundle exec bin/aicli --help
154
+ bundle exec bin/aicli config set OPENAI_KEY=<your token>
155
+ bundle exec bin/aicli list all log files
156
+ ```
157
+
158
+ ## Common Issues
159
+
160
+ ### 429 error
161
+
162
+ This is due to incorrect billing setup or excessive quota usage. Please follow [this guide](https://help.openai.com/en/articles/6891831-error-code-429-you-exceeded-your-current-quota-please-check-your-plan-and-billing-details) to fix it.
163
+
164
+ You can activate billing at [this link](https://platform.openai.com/account/billing/overview).
165
+
166
+ ## Motivation
167
+
168
+ I am not a bash wizard, and am dying for access to the copilot CLI, and got impatient.
169
+
170
+ ## Credit
171
+
172
+ - Ruby port by [Antonio Molinari](https://github.com/magnum)
173
+ - Inspired by [BuilderIO/ai-shell](https://github.com/BuilderIO/ai-shell)
174
+ - Thanks to GitHub Copilot for their amazing tools and the idea for this
175
+ - Thanks to Hassan and his work on [aicommits](https://github.com/Nutlope/aicommits), which inspired the workflow and some parts of the code and flows
176
+ - Original Node.js implementation by [Builder.io](https://www.builder.io)
data/bin/ai ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/aicli'
5
+
6
+ AiCli::CLI.start(ARGV)
data/bin/aicli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/aicli'
5
+
6
+ AiCli::CLI.start(ARGV)
data/lib/aicli/cli.rb ADDED
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'pastel'
5
+
6
+ module AiCli
7
+ class CLI < Thor
8
+ KNOWN_COMMANDS = %w[config chat update help version tree].freeze
9
+
10
+ def self.exit_on_failure?
11
+ true
12
+ end
13
+
14
+ def self.start(given_args = ARGV, config = {})
15
+ args = given_args.dup
16
+
17
+ # Extract global flags that may appear before the prompt text
18
+ silent = args.delete('--silent') || args.delete('-s')
19
+ version_flag = args.delete('--version') || args.delete('-v')
20
+ help_flag = args.delete('--help') || args.delete('-h')
21
+
22
+ if version_flag
23
+ puts AiCli::VERSION
24
+ return
25
+ end
26
+
27
+ if help_flag && (args.empty? || !KNOWN_COMMANDS.include?(args.first))
28
+ new.help
29
+ return
30
+ end
31
+
32
+ # Prompt flag: -p / --prompt
33
+ prompt_from_flag = nil
34
+ if (idx = args.index('-p') || args.index('--prompt'))
35
+ prompt_from_flag = args[idx + 1]
36
+ args.slice!(idx, 2)
37
+ end
38
+
39
+ first = args.first
40
+
41
+ if first.nil? || !KNOWN_COMMANDS.include?(first)
42
+ # Treat remaining args as the natural-language prompt
43
+ prompt_text = prompt_from_flag || args.join(' ')
44
+ invoke_prompt(prompt_text, silent: !silent.nil?)
45
+ return
46
+ end
47
+
48
+ # Rebuild argv for Thor subcommands, preserving silent if relevant
49
+ thor_args = args
50
+ thor_args = ['--silent'] + thor_args if silent && first == 'chat'
51
+ super(thor_args, config)
52
+ end
53
+
54
+ def self.invoke_prompt(prompt_text, silent: false)
55
+ pastel = Pastel.new
56
+
57
+ begin
58
+ config = Helpers::Config.get
59
+ Helpers::I18n.set_language(config['LANGUAGE'])
60
+ rescue StandardError
61
+ Helpers::I18n.set_language('en')
62
+ end
63
+
64
+ Prompt.run(use_prompt: prompt_text, silent_mode: silent)
65
+ rescue Helpers::KnownError, StandardError => e
66
+ puts "\n#{pastel.red('✖')} #{e.message}"
67
+ Helpers::Error.handle_cli_error(e)
68
+ exit 1
69
+ end
70
+
71
+ desc 'config [MODE] [KEY=VALUE...]', 'Configure the CLI'
72
+ def config(mode = nil, *key_values)
73
+ init_i18n
74
+ Commands::Config.run(mode, *key_values)
75
+ end
76
+
77
+ desc 'chat', 'Start a new chat session'
78
+ def chat
79
+ init_i18n
80
+ Commands::Chat.run
81
+ end
82
+
83
+ desc 'update', 'Update aicli to the latest version'
84
+ def update
85
+ init_i18n
86
+ Commands::Update.run
87
+ end
88
+
89
+ map %w[--version -v] => :__print_version
90
+ desc 'version', 'Print version'
91
+ def __print_version
92
+ puts AiCli::VERSION
93
+ end
94
+
95
+ private
96
+
97
+ def init_i18n
98
+ config = Helpers::Config.get
99
+ Helpers::I18n.set_language(config['LANGUAGE'])
100
+ rescue StandardError
101
+ Helpers::I18n.set_language('en')
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+ require 'tty-prompt'
5
+ require 'tty-spinner'
6
+
7
+ module AiCli
8
+ module Commands
9
+ module Chat
10
+ module_function
11
+
12
+ def run
13
+ config = Helpers::Config.get
14
+ key = config['OPENAI_KEY']
15
+ model = config['MODEL']
16
+ api_endpoint = config['OPENAI_API_ENDPOINT']
17
+ chat_history = []
18
+
19
+ pastel = Pastel.new
20
+ prompt = TTY::Prompt.new(interrupt: :exit)
21
+
22
+ puts ''
23
+ puts "┌ #{Helpers::I18n.t('Starting new conversation')}"
24
+
25
+ loop do
26
+ user_prompt = prompt.ask(pastel.cyan("#{Helpers::I18n.t('You')}:")) do |q|
27
+ q.required true
28
+ q.validate(/.+/, Helpers::I18n.t('Please enter a prompt.'))
29
+ end
30
+
31
+ if user_prompt.nil? || user_prompt == 'exit'
32
+ puts "└ #{Helpers::I18n.t('Goodbye!')}"
33
+ exit 0
34
+ end
35
+
36
+ spinner = TTY::Spinner.new("[:spinner] #{Helpers::I18n.t('THINKING...')}", format: :dots)
37
+ spinner.auto_spin
38
+
39
+ chat_history << { 'role' => 'user', 'content' => user_prompt }
40
+
41
+ stream_lines = Helpers::Completion.generate_completion(
42
+ prompt: chat_history,
43
+ key: key,
44
+ model: model,
45
+ api_endpoint: api_endpoint
46
+ )
47
+ enumerator = stream_lines.each
48
+ read_response = Helpers::Completion.read_data(enumerator)
49
+
50
+ spinner.success(pastel.green('aicli:'))
51
+ puts ''
52
+ full_response = read_response.call(->(chunk) { print chunk })
53
+ chat_history << { 'role' => 'assistant', 'content' => full_response }
54
+ puts ''
55
+ puts ''
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+
5
+ module AiCli
6
+ module Commands
7
+ module Config
8
+ module_function
9
+
10
+ def run(mode = nil, *key_values)
11
+ pastel = Pastel.new
12
+
13
+ if mode.nil? || mode == 'ui'
14
+ Helpers::Config.show_config_ui
15
+ return
16
+ end
17
+
18
+ if key_values.empty?
19
+ puts "#{Helpers::I18n.t('Error')}: #{Helpers::I18n.t('Missing required parameter')} \"key=value\"\n"
20
+ exit 1
21
+ end
22
+
23
+ case mode
24
+ when 'get'
25
+ config = Helpers::Config.get
26
+ key_values.each do |key|
27
+ if Helpers::Config.has_own?(config, key)
28
+ puts "#{key}=#{config[key]}"
29
+ else
30
+ raise Helpers::KnownError, "#{Helpers::I18n.t('Invalid config property')}: #{key}"
31
+ end
32
+ end
33
+ when 'set'
34
+ pairs = key_values.map do |kv|
35
+ k, v = kv.split('=', 2)
36
+ [k, v]
37
+ end
38
+ Helpers::Config.set(pairs)
39
+ else
40
+ raise Helpers::KnownError, "#{Helpers::I18n.t('Invalid mode')}: #{mode}"
41
+ end
42
+ rescue Helpers::KnownError, StandardError => e
43
+ puts "\n#{pastel.red('✖')} #{e.message}"
44
+ Helpers::Error.handle_cli_error(e)
45
+ exit 1
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+
5
+ module AiCli
6
+ module Commands
7
+ module Update
8
+ module_function
9
+
10
+ def run
11
+ pastel = Pastel.new
12
+ puts ''
13
+ command = 'gem update aicli'
14
+ puts pastel.dim("#{Helpers::I18n.t('Running')}: #{command}")
15
+ puts ''
16
+ system(command)
17
+ puts ''
18
+ end
19
+ end
20
+ end
21
+ end