aicli 0.3.0 → 0.4.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 +4 -4
- data/README.md +23 -154
- data/lib/aicli/cli.rb +11 -32
- data/lib/aicli/commands/chat.rb +15 -37
- data/lib/aicli/commands/config.rb +29 -18
- data/lib/aicli/helpers/completion.rb +65 -103
- data/lib/aicli/helpers/config.rb +1 -5
- data/lib/aicli/helpers/constants.rb +1 -0
- data/lib/aicli/helpers/context.rb +1 -2
- data/lib/aicli/helpers/error.rb +1 -3
- data/lib/aicli/helpers/i18n.rb +6 -0
- data/lib/aicli/helpers/script_action_menu.rb +128 -0
- data/lib/aicli/helpers/script_editor.rb +65 -0
- data/lib/aicli/helpers/shell_exec.rb +14 -0
- data/lib/aicli/helpers/shell_history.rb +164 -27
- data/lib/aicli/helpers/theme.rb +40 -0
- data/lib/aicli/prompt.rb +77 -142
- data/lib/aicli/version.rb +1 -1
- data/lib/aicli.rb +4 -1
- data/locales/en.yml +11 -36
- data/locales/it.yml +14 -38
- metadata +19 -2
- data/lib/aicli/helpers/strip_regex_patterns.rb +0 -21
|
@@ -3,45 +3,182 @@
|
|
|
3
3
|
module AiCli
|
|
4
4
|
module Helpers
|
|
5
5
|
module ShellHistory
|
|
6
|
-
|
|
6
|
+
SOURCE_MARKER = '# aicli shell history integration'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
history_file = history_file_path
|
|
10
|
-
return unless history_file
|
|
8
|
+
module_function
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
# Queue the executed command for the shell wrapper to store in history
|
|
11
|
+
# instead of the original `ai ...` invocation.
|
|
12
|
+
def record_executed(command)
|
|
13
|
+
text = command.to_s.strip
|
|
14
|
+
return if text.empty?
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Config.ensure_home!
|
|
17
|
+
File.write(pending_path, text)
|
|
16
18
|
rescue StandardError
|
|
17
|
-
|
|
19
|
+
nil
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
def install_shell_integration!
|
|
23
|
+
Config.ensure_home!
|
|
24
|
+
target_dir = Config.home_dir
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
'ai.zsh' => zsh_wrapper,
|
|
28
|
+
'ai.bash' => bash_wrapper
|
|
29
|
+
}.each do |name, contents|
|
|
30
|
+
File.write(File.join(target_dir, name), contents)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def activate_shell_integration!
|
|
35
|
+
install_shell_integration!
|
|
36
|
+
rc_path = shell_rc_path
|
|
37
|
+
source_path = wrapper_source_path
|
|
38
|
+
|
|
39
|
+
if rc_path && File.exist?(rc_path)
|
|
40
|
+
append_source_to_rc!(rc_path, source_path)
|
|
41
|
+
{ rc_path: rc_path, source_path: source_path, activated: true }
|
|
42
|
+
else
|
|
43
|
+
{ rc_path: nil, source_path: source_path, activated: false }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def append_source_to_rc!(rc_path, source_path)
|
|
48
|
+
content = File.read(rc_path)
|
|
49
|
+
return if content.include?(SOURCE_MARKER) || content.include?(source_path)
|
|
50
|
+
|
|
51
|
+
block = <<~RC
|
|
52
|
+
|
|
53
|
+
#{SOURCE_MARKER}
|
|
54
|
+
[[ -f #{source_path} ]] && source #{source_path}
|
|
55
|
+
RC
|
|
56
|
+
File.write(rc_path, content.rstrip + block)
|
|
57
|
+
end
|
|
23
58
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
59
|
+
def shell_rc_path
|
|
60
|
+
home = Dir.home
|
|
61
|
+
case current_shell
|
|
27
62
|
when 'zsh'
|
|
28
|
-
File.join(home, '.
|
|
29
|
-
when '
|
|
30
|
-
File.join(home, '.
|
|
31
|
-
when 'ksh'
|
|
32
|
-
File.join(home, '.ksh_history')
|
|
33
|
-
when 'tcsh'
|
|
34
|
-
File.join(home, '.history')
|
|
63
|
+
File.join(home, '.zshrc')
|
|
64
|
+
when 'bash'
|
|
65
|
+
File.join(home, '.bashrc')
|
|
35
66
|
end
|
|
36
67
|
end
|
|
37
68
|
|
|
38
|
-
def
|
|
39
|
-
|
|
69
|
+
def wrapper_source_path
|
|
70
|
+
name = current_shell == 'bash' ? 'ai.bash' : 'ai.zsh'
|
|
71
|
+
File.join(Config.home_dir, name)
|
|
72
|
+
end
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
74
|
+
def shell_integration_hint(activated: false, rc_path: nil, source_path: nil)
|
|
75
|
+
source_path ||= wrapper_source_path
|
|
76
|
+
if activated && rc_path
|
|
77
|
+
<<~HINT.strip
|
|
78
|
+
Added to #{rc_path}. Reload your shell:
|
|
79
|
+
|
|
80
|
+
source #{rc_path}
|
|
81
|
+
|
|
82
|
+
Or open a new terminal tab.
|
|
83
|
+
HINT
|
|
84
|
+
else
|
|
85
|
+
case current_shell
|
|
86
|
+
when 'zsh'
|
|
87
|
+
<<~HINT.strip
|
|
88
|
+
Add to ~/.zshrc for shell history integration:
|
|
89
|
+
|
|
90
|
+
source #{source_path}
|
|
91
|
+
HINT
|
|
92
|
+
when 'bash'
|
|
93
|
+
<<~HINT.strip
|
|
94
|
+
Add to ~/.bashrc for shell history integration:
|
|
95
|
+
|
|
96
|
+
source #{source_path}
|
|
97
|
+
HINT
|
|
98
|
+
else
|
|
99
|
+
''
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def pending_path
|
|
105
|
+
File.join(Config.home_dir, 'history.pending')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def current_shell
|
|
109
|
+
File.basename(ENV['SHELL'] || 'bash')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def zsh_wrapper
|
|
113
|
+
<<~'ZSH'
|
|
114
|
+
# aicli shell history integration for zsh
|
|
115
|
+
if [[ -z "$AICLI_ZSH_INTEGRATION" ]]; then
|
|
116
|
+
AICLI_ZSH_INTEGRATION=1
|
|
117
|
+
|
|
118
|
+
_aicli_zshaddhistory() {
|
|
119
|
+
if [[ -n "$AICLI_PENDING_HISTORY" ]]; then
|
|
120
|
+
print -s -- "$AICLI_PENDING_HISTORY"
|
|
121
|
+
unset AICLI_PENDING_HISTORY
|
|
122
|
+
return 1
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_aicli_precmd() {
|
|
127
|
+
if [[ -n "$AICLI_PENDING_HISTORY" ]]; then
|
|
128
|
+
local cmd="$AICLI_PENDING_HISTORY"
|
|
129
|
+
unset AICLI_PENDING_HISTORY
|
|
130
|
+
local last="${${(z)${(f)"$(fc -l -1 2>/dev/null)"}}[2]}"
|
|
131
|
+
if [[ "$last" == ai\ * || "$last" == aicli\ * ]]; then
|
|
132
|
+
fc -d -1 -I 2>/dev/null
|
|
133
|
+
fi
|
|
134
|
+
print -s -- "$cmd"
|
|
135
|
+
fi
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
ai() {
|
|
139
|
+
AICLI_SHELL_WRAPPER=1 command ai "$@"
|
|
140
|
+
local ret=$?
|
|
141
|
+
if [[ -f "$HOME/.aicli/history.pending" ]]; then
|
|
142
|
+
typeset -g AICLI_PENDING_HISTORY="${$(<"$HOME/.aicli/history.pending")}"
|
|
143
|
+
rm -f "$HOME/.aicli/history.pending"
|
|
144
|
+
fi
|
|
145
|
+
return $ret
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
zshaddhistory_functions+=(_aicli_zshaddhistory)
|
|
149
|
+
precmd_functions+=(_aicli_precmd)
|
|
150
|
+
fi
|
|
151
|
+
ZSH
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def bash_wrapper
|
|
155
|
+
<<~'BASH'
|
|
156
|
+
# aicli shell history integration for bash
|
|
157
|
+
if [[ -z "$AICLI_BASH_INTEGRATION" ]]; then
|
|
158
|
+
AICLI_BASH_INTEGRATION=1
|
|
159
|
+
|
|
160
|
+
_aicli_bash_history() {
|
|
161
|
+
if [[ -n "$AICLI_PENDING_HISTORY" ]]; then
|
|
162
|
+
local cmd="$AICLI_PENDING_HISTORY"
|
|
163
|
+
unset AICLI_PENDING_HISTORY
|
|
164
|
+
history -d "$HISTCMD" 2>/dev/null
|
|
165
|
+
history -s "$cmd"
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ai() {
|
|
170
|
+
AICLI_SHELL_WRAPPER=1 command ai "$@"
|
|
171
|
+
local ret=$?
|
|
172
|
+
if [[ -f "$HOME/.aicli/history.pending" ]]; then
|
|
173
|
+
AICLI_PENDING_HISTORY=$(<"$HOME/.aicli/history.pending")
|
|
174
|
+
rm -f "$HOME/.aicli/history.pending"
|
|
175
|
+
fi
|
|
176
|
+
return $ret
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
PROMPT_COMMAND="_aicli_bash_history${PROMPT_COMMAND:+; $PROMPT_COMMAND}"
|
|
180
|
+
fi
|
|
181
|
+
BASH
|
|
45
182
|
end
|
|
46
183
|
end
|
|
47
184
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pastel'
|
|
4
|
+
require 'tty-prompt'
|
|
5
|
+
|
|
6
|
+
module AiCli
|
|
7
|
+
module Helpers
|
|
8
|
+
module Theme
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def accent(text)
|
|
12
|
+
pastel.public_send(Constants::ACCENT_COLOR, text)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def bold(text)
|
|
16
|
+
pastel.bold(text)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dim(text)
|
|
20
|
+
pastel.dim(text)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def status(config)
|
|
24
|
+
"#{Constants::PROJECT_NAME}, #{config['PROVIDER']} / #{config['MODEL']}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def show_status(config)
|
|
28
|
+
puts accent(status(config))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def prompt(**options)
|
|
32
|
+
TTY::Prompt.new(active_color: Constants::ACCENT_COLOR, **options)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def pastel
|
|
36
|
+
@pastel ||= Pastel.new
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/aicli/prompt.rb
CHANGED
|
@@ -1,102 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'open3'
|
|
4
3
|
require 'clipboard'
|
|
5
|
-
require 'pastel'
|
|
6
|
-
require 'tty-prompt'
|
|
7
|
-
require 'tty-spinner'
|
|
8
4
|
|
|
9
5
|
module AiCli
|
|
10
6
|
module Prompt
|
|
11
7
|
module_function
|
|
12
8
|
|
|
13
9
|
def run(use_prompt: nil, silent_mode: false)
|
|
14
|
-
init_i18n
|
|
15
|
-
|
|
16
10
|
config = Helpers::Config.get
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
pastel = Pastel.new
|
|
20
|
-
puts ''
|
|
21
|
-
puts pastel.cyan(Helpers::Constants::PROJECT_NAME)
|
|
22
|
-
puts pastel.dim("#{config['PROVIDER']} / #{config['MODEL']}")
|
|
23
|
-
|
|
24
|
-
the_prompt = use_prompt.nil? || use_prompt.strip.empty? ? ask_prompt : use_prompt
|
|
11
|
+
prompt_text = use_prompt.to_s.strip.empty? ? ask_prompt : use_prompt
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
prompt: the_prompt,
|
|
31
|
-
config: config
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
spinner.success(Helpers::I18n.t('Your script') + ':')
|
|
35
|
-
puts ''
|
|
36
|
-
script = result[:read_script].call(->(chunk) { print chunk })
|
|
37
|
-
puts ''
|
|
38
|
-
puts ''
|
|
39
|
-
|
|
40
|
-
explanation_text = ''
|
|
41
|
-
unless skip_explanation
|
|
42
|
-
spinner = TTY::Spinner.new("[:spinner] #{Helpers::I18n.t('Getting explanation...')}", format: :dots)
|
|
43
|
-
spinner.auto_spin
|
|
44
|
-
|
|
45
|
-
info = result[:read_info].call(->(chunk) { print chunk })
|
|
46
|
-
if info.nil? || info.empty?
|
|
47
|
-
explanation = Helpers::Completion.get_explanation(
|
|
48
|
-
script: script,
|
|
49
|
-
config: config
|
|
50
|
-
)
|
|
51
|
-
spinner.success(Helpers::I18n.t('Explanation') + ':')
|
|
52
|
-
puts ''
|
|
53
|
-
explanation_text = explanation[:read_explanation].call(->(chunk) { print chunk })
|
|
54
|
-
puts ''
|
|
55
|
-
puts ''
|
|
56
|
-
else
|
|
57
|
-
explanation_text = info
|
|
58
|
-
spinner.success(Helpers::I18n.t('Explanation') + ':')
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
append_prompt_context(the_prompt, script, explanation_text)
|
|
63
|
-
run_or_revise_flow(script, config, silent_mode)
|
|
13
|
+
Helpers::Theme.show_status(config)
|
|
14
|
+
script = Helpers::Completion.get_script(prompt: prompt_text, config: config)
|
|
15
|
+
save_context(prompt_text, script, '')
|
|
16
|
+
present_script_menu(script, config, silent_mode, leading_newline: true)
|
|
64
17
|
end
|
|
65
18
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
Helpers::I18n.set_language(config['LANGUAGE'])
|
|
69
|
-
rescue StandardError
|
|
70
|
-
Helpers::I18n.set_language('en')
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def examples
|
|
74
|
-
[
|
|
75
|
-
Helpers::I18n.t('delete all log files'),
|
|
76
|
-
Helpers::I18n.t('list js files'),
|
|
77
|
-
Helpers::I18n.t('fetch me a random joke'),
|
|
78
|
-
Helpers::I18n.t('list all commits')
|
|
79
|
-
]
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def ask_prompt(initial = nil)
|
|
83
|
-
prompt = TTY::Prompt.new(interrupt: :error)
|
|
19
|
+
def ask_prompt
|
|
20
|
+
prompt = Helpers::Theme.prompt(interrupt: :error)
|
|
84
21
|
Helpers::Context.seed_prompt_history(prompt)
|
|
85
22
|
|
|
86
23
|
loop do
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
q.validate(/.+/, Helpers::I18n.t('Please enter a prompt.'))
|
|
92
|
-
end
|
|
93
|
-
rescue TTY::Reader::InputInterrupt
|
|
94
|
-
print "\r\e[2K"
|
|
24
|
+
return prompt.ask(Helpers::I18n.t('What would you like me to do?')) do |q|
|
|
25
|
+
q.required true
|
|
26
|
+
q.default Helpers::I18n.t('Say hello')
|
|
27
|
+
q.validate(/.+/, Helpers::I18n.t('Please enter a prompt.'))
|
|
95
28
|
end
|
|
29
|
+
rescue TTY::Reader::InputInterrupt
|
|
30
|
+
print "\r\e[2K"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ask_revision
|
|
35
|
+
Helpers::Theme.prompt(interrupt: :exit).ask(
|
|
36
|
+
Helpers::I18n.t('What would you like me to change in this script?')
|
|
37
|
+
) do |q|
|
|
38
|
+
q.required true
|
|
39
|
+
q.validate(/.+/, Helpers::I18n.t('Please enter a prompt.'))
|
|
96
40
|
end
|
|
97
41
|
end
|
|
98
42
|
|
|
99
|
-
def
|
|
43
|
+
def save_context(user_prompt, script, explanation = '')
|
|
100
44
|
assistant = +"```\n#{script}\n```"
|
|
101
45
|
assistant << "\n\n#{explanation}" unless explanation.to_s.strip.empty?
|
|
102
46
|
|
|
@@ -106,43 +50,65 @@ module AiCli
|
|
|
106
50
|
Helpers::Context.save_messages(messages)
|
|
107
51
|
end
|
|
108
52
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
53
|
+
def append_explanation_to_context(explanation)
|
|
54
|
+
text = explanation.to_s.strip
|
|
55
|
+
return if text.empty?
|
|
56
|
+
|
|
57
|
+
messages = Helpers::Context.load_messages
|
|
58
|
+
return if messages.empty?
|
|
59
|
+
|
|
60
|
+
last = messages.last
|
|
61
|
+
return unless last['role'] == 'assistant' && !last['content'].include?(text)
|
|
62
|
+
|
|
63
|
+
last['content'] = "#{last['content']}\n\n#{text}"
|
|
64
|
+
Helpers::Context.save_messages(messages)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def display_script(script, leading_newline: false)
|
|
68
|
+
text = script.to_s.strip
|
|
69
|
+
return if text.empty?
|
|
70
|
+
|
|
71
|
+
puts '' if leading_newline
|
|
72
|
+
puts Helpers::Theme.bold(text)
|
|
115
73
|
end
|
|
116
74
|
|
|
117
|
-
def
|
|
118
|
-
|
|
75
|
+
def display_explanation(text)
|
|
76
|
+
stripped = text.to_s.strip
|
|
77
|
+
return if stripped.empty?
|
|
78
|
+
|
|
119
79
|
puts ''
|
|
120
|
-
|
|
121
|
-
Helpers::ShellHistory.append(script)
|
|
80
|
+
puts Helpers::Theme.dim(stripped)
|
|
122
81
|
end
|
|
123
82
|
|
|
124
|
-
def
|
|
125
|
-
|
|
126
|
-
|
|
83
|
+
def present_script_menu(script, config, silent_mode, leading_newline: false, show_script: true)
|
|
84
|
+
display_script(script, leading_newline: leading_newline) if show_script
|
|
85
|
+
run_action_menu(script, config, silent_mode)
|
|
86
|
+
end
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
choices << { name: "📝 #{Helpers::I18n.t('Edit')}", value: :edit }
|
|
132
|
-
end
|
|
133
|
-
choices << { name: "🔁 #{Helpers::I18n.t('Revise')}", value: :revise }
|
|
134
|
-
choices << { name: "📋 #{Helpers::I18n.t('Copy')}", value: :copy }
|
|
135
|
-
choices << { name: "❌ #{Helpers::I18n.t('Cancel')}", value: :cancel }
|
|
88
|
+
def run_action_menu(script, config, silent_mode)
|
|
89
|
+
empty = script.strip.empty?
|
|
90
|
+
message = empty ? Helpers::I18n.t('Revise this script?') : Helpers::I18n.t('Run this script?')
|
|
136
91
|
|
|
137
|
-
|
|
138
|
-
answer =
|
|
92
|
+
puts ''
|
|
93
|
+
answer = Helpers::ScriptActionMenu.select(
|
|
94
|
+
question: message,
|
|
95
|
+
config: config,
|
|
96
|
+
silent_mode: silent_mode,
|
|
97
|
+
empty_script: empty
|
|
98
|
+
)
|
|
139
99
|
|
|
140
100
|
case answer
|
|
141
101
|
when :yes
|
|
142
|
-
|
|
102
|
+
puts ''
|
|
103
|
+
Helpers::ShellExec.run(script)
|
|
143
104
|
when :edit
|
|
144
|
-
|
|
145
|
-
|
|
105
|
+
edited = Helpers::ScriptEditor.ask(script)
|
|
106
|
+
present_script_menu(edited, config, silent_mode, show_script: false) if edited && !edited.empty?
|
|
107
|
+
when :explain
|
|
108
|
+
text = Helpers::Completion.get_explanation(script: script, config: config)
|
|
109
|
+
display_explanation(text)
|
|
110
|
+
append_explanation_to_context(text)
|
|
111
|
+
present_script_menu(script, config, silent_mode, leading_newline: !text.to_s.strip.empty?)
|
|
146
112
|
when :revise
|
|
147
113
|
revision_flow(script, config, silent_mode)
|
|
148
114
|
when :copy
|
|
@@ -154,45 +120,14 @@ module AiCli
|
|
|
154
120
|
end
|
|
155
121
|
end
|
|
156
122
|
|
|
157
|
-
def revision_flow(
|
|
123
|
+
def revision_flow(script, config, silent_mode)
|
|
158
124
|
revision = ask_revision
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
spinner.auto_spin
|
|
162
|
-
|
|
163
|
-
result = Helpers::Completion.get_revision(
|
|
164
|
-
prompt: revision,
|
|
165
|
-
code: current_script,
|
|
166
|
-
config: config
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
spinner.success(Helpers::I18n.t('Your new script') + ':')
|
|
170
|
-
puts ''
|
|
171
|
-
script = result[:read_script].call(->(chunk) { print chunk })
|
|
172
|
-
puts ''
|
|
173
|
-
puts ''
|
|
174
|
-
|
|
175
|
-
unless silent_mode
|
|
176
|
-
info_spinner = TTY::Spinner.new("[:spinner] #{Helpers::I18n.t('Getting explanation...')}", format: :dots)
|
|
177
|
-
info_spinner.auto_spin
|
|
178
|
-
|
|
179
|
-
explanation = Helpers::Completion.get_explanation(
|
|
180
|
-
script: script,
|
|
181
|
-
config: config
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
info_spinner.success(Helpers::I18n.t('Explanation') + ':')
|
|
185
|
-
puts ''
|
|
186
|
-
explanation[:read_explanation].call(->(chunk) { print chunk })
|
|
187
|
-
puts ''
|
|
188
|
-
puts ''
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
run_or_revise_flow(script, config, silent_mode)
|
|
125
|
+
revised = Helpers::Completion.get_revision(prompt: revision, code: script, config: config)
|
|
126
|
+
present_script_menu(revised, config, silent_mode, leading_newline: true)
|
|
192
127
|
end
|
|
193
128
|
|
|
194
|
-
private_class_method :
|
|
195
|
-
:
|
|
196
|
-
:
|
|
129
|
+
private_class_method :ask_prompt, :ask_revision, :save_context, :append_explanation_to_context,
|
|
130
|
+
:display_script, :display_explanation, :present_script_menu,
|
|
131
|
+
:run_action_menu, :revision_flow
|
|
197
132
|
end
|
|
198
133
|
end
|
data/lib/aicli/version.rb
CHANGED
data/lib/aicli.rb
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'aicli/version'
|
|
4
4
|
require_relative 'aicli/helpers/constants'
|
|
5
|
+
require_relative 'aicli/helpers/theme'
|
|
6
|
+
require_relative 'aicli/helpers/script_action_menu'
|
|
5
7
|
require_relative 'aicli/helpers/error'
|
|
6
8
|
require_relative 'aicli/helpers/i18n'
|
|
7
9
|
require_relative 'aicli/helpers/os_detect'
|
|
8
|
-
require_relative 'aicli/helpers/strip_regex_patterns'
|
|
9
10
|
require_relative 'aicli/helpers/shell_history'
|
|
11
|
+
require_relative 'aicli/helpers/shell_exec'
|
|
12
|
+
require_relative 'aicli/helpers/script_editor'
|
|
10
13
|
require_relative 'aicli/helpers/llm'
|
|
11
14
|
require_relative 'aicli/helpers/completion'
|
|
12
15
|
require_relative 'aicli/helpers/config'
|
data/locales/en.yml
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
Starting new conversation: Starting new conversation
|
|
3
2
|
Goodbye!: Goodbye!
|
|
4
|
-
send a message ('exit' to quit): send a message ('exit' to quit)
|
|
5
3
|
Please enter a prompt.: Please enter a prompt.
|
|
6
4
|
THINKING...: THINKING...
|
|
7
|
-
Please set your OpenAI API key via `ai config set OPENAI_KEY=<your token>`: Please
|
|
8
|
-
set your OpenAI API key via `ai config set OPENAI_KEY=<your token>`
|
|
9
5
|
Set config: Set config
|
|
10
6
|
Enter your OpenAI API key: Enter your OpenAI API key
|
|
11
7
|
Enter your Anthropic API key: Enter your Anthropic API key
|
|
@@ -14,7 +10,6 @@ Provider: Provider
|
|
|
14
10
|
Pick a provider: Pick a provider
|
|
15
11
|
OpenAI Key: OpenAI Key
|
|
16
12
|
Anthropic Key: Anthropic Key
|
|
17
|
-
Please enter a key: Please enter a key
|
|
18
13
|
OpenAI API Endpoint: OpenAI API Endpoint
|
|
19
14
|
Enter your OpenAI API Endpoint: Enter your OpenAI API Endpoint
|
|
20
15
|
Silent Mode: Silent Mode
|
|
@@ -22,50 +17,30 @@ Enable silent mode?: Enable silent mode?
|
|
|
22
17
|
Model: Model
|
|
23
18
|
Pick a model.: Pick a model.
|
|
24
19
|
No models found for this provider.: No models found for this provider.
|
|
25
|
-
Enter the model you want to use: Enter the model you want to use
|
|
26
20
|
Language: Language
|
|
27
21
|
Enter the language you want to use: Enter the language you want to use
|
|
28
22
|
What would you like me to do?: What would you like me to do?
|
|
29
|
-
delete all log files: delete all log files
|
|
30
|
-
list js files: list js files
|
|
31
|
-
fetch me a random joke: fetch me a random joke
|
|
32
|
-
list all commits: list all commits
|
|
33
23
|
Say hello: Say hello
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
e.g.: e.g.
|
|
38
|
-
e.g. change the folder name: e.g. change the folder name
|
|
39
|
-
Your script: Your script
|
|
40
|
-
Loading...: Loading...
|
|
41
|
-
Getting explanation...: Getting explanation...
|
|
42
|
-
Explanation: Explanation
|
|
24
|
+
Edit script: Edit script
|
|
25
|
+
Edit with arrow keys, Enter to confirm, Ctrl+C to cancel: Edit with arrow keys, Enter to confirm, Ctrl+C to cancel
|
|
26
|
+
What would you like me to change in this script?: What would you like me to change in this script?
|
|
43
27
|
Run this script?: Run this script?
|
|
44
|
-
Run it?: Run it?
|
|
45
28
|
Revise this script?: Revise this script?
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
Yes: Yes
|
|
30
|
+
Modify: Modify
|
|
31
|
+
Exit: Exit
|
|
32
|
+
Explain: Explain
|
|
50
33
|
Revise: Revise
|
|
51
|
-
Give feedback via prompt and get a new result: Give feedback via prompt and get a
|
|
52
|
-
new result
|
|
53
34
|
Copy: Copy
|
|
54
|
-
Copy the generated script to your clipboard: Copy the generated script to your clipboard
|
|
55
35
|
Cancel: Cancel
|
|
56
|
-
Exit the program: Exit the program
|
|
57
36
|
Running: Running
|
|
58
37
|
Copied to clipboard!: Copied to clipboard!
|
|
59
|
-
Your new script: Your new script
|
|
60
38
|
Invalid config property: Invalid config property
|
|
39
|
+
Invalid mode: Invalid mode
|
|
61
40
|
Shell detection failed unexpectedly: Shell detection failed unexpectedly
|
|
62
|
-
Invalid model: Invalid model
|
|
63
41
|
Error: Error
|
|
64
42
|
Missing required parameter: Missing required parameter
|
|
65
|
-
Please open a Bug report with the information above: Please open a Bug report with
|
|
66
|
-
the information above
|
|
67
|
-
Prompt to run: Prompt to run
|
|
43
|
+
Please open a Bug report with the information above: Please open a Bug report with the information above
|
|
68
44
|
You: You
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
messages: messages
|
|
45
|
+
Shell integration installed.: Shell integration installed.
|
|
46
|
+
Run it?: Run it?
|