ruby-shell 2.7.0 → 2.9.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -2
  3. data/bin/rsh +369 -34
  4. metadata +7 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35706aa5efb5ce661a102e1c6a5b22638a750b567f41930b683f239d42386964
4
- data.tar.gz: 164ea239fc7081182c031cf002dc7e3c64696536f5795057e24ba027a899f998
3
+ metadata.gz: 3bfe09de4762828310479cc0535ec0942b575fc331d4f16bfc1614861cf14b8c
4
+ data.tar.gz: 264e440a9257eb49e7985a034fbfc8ec7a3be5fb1efc9e046d4ef7cf94116872
5
5
  SHA512:
6
- metadata.gz: 952d3ba93d41715d50a6b8a4f669f8f670f3b5c403f300f24fd7a28fe9cb432a4f66b2ba747fb9ddbab6e3ec6a33ec94113c3e499ee6014a25ec6d7861266929
7
- data.tar.gz: 41ea80063e2fd754acd4a449ddac1f418bb3450b10bd70a9a99432159fefbcf2002bf6964638cd03079585088974d382c0978d830eb3beb8f28936cc2bc2cf65
6
+ metadata.gz: bc12e932035ded646ff4459c7d3ad299582102c3d208b8ddb1b529691f8cc21d02ec3e7d7c21b4a346b716eba495540361291b5e483438b712afe9c6fb0ea381
7
+ data.tar.gz: bf29a4a08d04835d7275a09c9bb0970d597a72a207a68e57e42755b433613dddac4d93bcae3a0152b74f6a8093562b8f748d8f33844bb736f0fdff66da83bfeb
data/README.md CHANGED
@@ -33,7 +33,22 @@ Or simply `gem install ruby-shell`.
33
33
  * All colors are themeable in .rshrc (see github link for possibilities)
34
34
  * Copy current command line to primary selection (paste w/middle button) with `Ctrl-y`
35
35
 
36
- ## NEW in v2.7.0 - Ruby Functions
36
+ ## NEW in v2.9.0 - AI Integration
37
+ * **AI-powered command assistance**: Get help with commands using natural language
38
+ * **`@ <question>`**: Ask questions and get AI-generated text responses
39
+ * **`@@ <request>`**: Describe what you want to do, and AI suggests the command
40
+ * **Smart command suggestion**: AI suggestions appear directly on the command line, ready to execute
41
+ * **Local AI support**: Works with Ollama for privacy-focused local AI
42
+ * **External AI support**: Configure OpenAI or other providers via `.rshrc`
43
+ * **Syntax highlighting**: @ and @@ commands are highlighted in blue
44
+
45
+ ## Enhanced Help System & Nick Management (v2.8.0)
46
+ * **Two-column help display**: Compact, organized help that fits on one screen
47
+ * **New `:info` command**: Shows introduction and feature overview
48
+ * **`:nickdel` and `:gnickdel`**: Intuitive commands to delete nicks and gnicks
49
+ * **Improved help organization**: Quick reference for keyboard shortcuts, commands, and features
50
+
51
+ ## Ruby Functions (v2.7.0)
37
52
  * **Define Ruby functions as shell commands**: `:defun 'weather(*args) = system("curl -s wttr.in/#{args[0] || \"oslo\"}")'`
38
53
  * **Call like any shell command**: `weather london`
39
54
  * **Full Ruby power**: Access to Ruby stdlib, file operations, JSON parsing, web requests, etc.
@@ -58,13 +73,16 @@ Special functions/integrations:
58
73
  Special commands:
59
74
  * `:nick 'll = ls -l'` to make a command alias (ll) point to a command (ls -l)
60
75
  * `:gnick 'h = /home/me'` to make a general alias (h) point to something (/home/me)
76
+ * `:nickdel 'name'` to delete a command nick (or use `:nick '-name'`)
77
+ * `:gnickdel 'name'` to delete a general nick (or use `:gnick '-name'`)
61
78
  * `:nick?` will list all command nicks and general nicks (you can edit your nicks in .rshrc)
62
79
  * `:history` will list the command history, while `:rmhistory` will delete the history
63
80
  * `:jobs` will list background jobs, `:fg [job_id]` brings jobs to foreground, `:bg [job_id]` resumes stopped jobs
64
81
  * `:defun 'func(args) = code'` defines Ruby functions callable as shell commands
65
82
  * `:defun?` lists all user-defined functions, `:defun '-func'` removes functions
83
+ * `:info` shows introduction and feature overview
66
84
  * `:version` Shows the rsh version number and the last published gem file version
67
- * `:help` will display this help text
85
+ * `:help` will display a compact command reference in two columns
68
86
 
69
87
  Background jobs:
70
88
  * Use `command &` to run commands in background
@@ -72,6 +90,21 @@ Background jobs:
72
90
  * Use `:fg` or `:fg job_id` to bring jobs to foreground
73
91
  * Use `Ctrl-Z` to suspend running jobs, `:bg job_id` to resume them
74
92
 
93
+ ## AI Configuration
94
+ The AI features work out of the box with Ollama for local AI processing. To set up:
95
+
96
+ ### Local AI (Recommended)
97
+ 1. Install Ollama: `curl -fsSL https://ollama.com/install.sh | sh`
98
+ 2. Pull a model: `ollama pull llama3.2`
99
+ 3. That's it! Use `@ What is the capital of France?` or `@@ list files by size`
100
+
101
+ ### External AI (OpenAI)
102
+ Add to your `.rshrc`:
103
+ ```ruby
104
+ @aimodel = "gpt-4"
105
+ @aikey = "your-api-key-here"
106
+ ```
107
+
75
108
  ## Moving around
76
109
  While you `cd` around to different directories, you can see the last 10 directories visited via the command `:dirs` or the convenient shortcut `#`. Entering the number in the list (like `6` and ENTER) will jump you to that directory. Entering `-` will jump you back to the previous dir (equivalent of `1`. Entering `~` will get you to your home dir. If you want to bookmark a special directory, you can do that via a general nick like this: `:gnick "x = /path/to/a/dir/"` - this would bookmark the directory to the single letter `x`.
77
110
 
data/bin/rsh CHANGED
@@ -8,7 +8,7 @@
8
8
  # Web_site: http://isene.com/
9
9
  # Github: https://github.com/isene/rsh
10
10
  # License: Public domain
11
- @version = "2.7.0" # Major release: Ruby functions, job control, command substitution, login shell support
11
+ @version = "2.9.0" # Feature release: AI integration with @ and @@ commands
12
12
 
13
13
  # MODULES, CLASSES AND EXTENSIONS
14
14
  class String # Add coloring to strings (with escaping for Readline)
@@ -109,20 +109,22 @@ begin # Initialization
109
109
  @dirs = ["."]*10
110
110
  @jobs = {} # Background jobs tracking
111
111
  @job_id = 0 # Job counter
112
+ @ai_suggestion = nil # Store AI command suggestion
112
113
  @last_exit = 0 # Last command exit status
113
114
  def pre_cmd; end # User-defined function to be run BEFORE command execution
114
115
  def post_cmd; end # User-defined function to be run AFTER command execution
115
116
  end
116
117
 
117
118
  # HELP TEXT
118
- @help = <<~HELP
119
+ @info = <<~INFO
119
120
 
120
121
  Hello #{@user}, welcome to rsh - the Ruby SHell.
121
122
 
122
123
  rsh does not attempt to compete with the grand old shells like bash and zsh.
123
124
  It serves the specific needs and wants of its author. If you like it, then feel free
124
- to ask for more or different features here: https://github.com/isene/rsh. Features:
125
-
125
+ to ask for more or different features here: https://github.com/isene/rsh.
126
+
127
+ Features:
126
128
  * Aliases (called nicks in rsh) - both for commands and general nicks
127
129
  * Syntax highlighting, matching nicks, system commands and valid dirs/files
128
130
  * Tab completions for nicks, system commands, command switches and dirs/files
@@ -137,41 +139,19 @@ end
137
139
  * rsh specific commands and full set of Ruby commands available via :<command>
138
140
  * All colors are themeable in .rshrc (see github link for possibilities)
139
141
  * Copy current command line to primary selection (paste w/middle button) with `Ctrl-y`
140
-
141
- Special functions/integrations:
142
- * Use `r` to launch rtfm (https://github.com/isene/RTFM) - if you have it installed
143
- * Use `f` to launch fzf (https://github.com/junegunn/fzf) - if you have it installed
144
- * Use `=` followed by xrpn commands separated by commas or double-spaces (https://github.com/isene/xrpn)
145
- * Use `:` followed by a Ruby expression to access the whole world of Ruby
146
-
147
- Special commands:
148
- * `:nick 'll = ls -l'` to make a command alias (ll) point to a command (ls -l)
149
- * `:gnick 'h = /home/me'` to make a general alias (h) point to something (/home/me)
150
- * `:nick?` will list all command nicks and general nicks (you can edit your nicks in .rshrc)
151
- * `:history` will list the command history, while `:rmhistory` will delete the history
152
- * `:jobs` will list background jobs, `:fg [job_id]` brings jobs to foreground, `:bg [job_id]` resumes stopped jobs
153
- * `:defun 'func(args) = code'` defines Ruby functions callable as shell commands
154
- * `:defun?` lists all user-defined functions, `:defun '-func'` removes functions
155
- * `:version` Shows the rsh version number and the last published gem file version
156
- * `:help` will display this help text
157
-
158
- Background jobs:
159
- * Use `command &` to run commands in background
160
- * Use `:jobs` to list active background jobs
161
- * Use `:fg` or `:fg job_id` to bring jobs to foreground
162
- * Use `Ctrl-Z` to suspend running jobs, `:bg job_id` to resume them
142
+ * AI integration: Use @ for text responses and @@ for command suggestions (requires ollama or OpenAI)
163
143
 
164
- Ruby Functions:
165
- * Define with `:defun 'myls(*args) = Dir.glob("*").each {|f| puts f}'`
166
- * Call like any shell command: `myls` or `myls arg1 arg2`
167
- * Functions have full access to Ruby stdlib and rsh internals
168
- * Remove with `:defun '-myls'` and list with `:defun?`
144
+ Use `:help` for command reference.
169
145
 
146
+ INFO
147
+
148
+ @help = <<~HELP
149
+
170
150
  HELP
171
151
 
172
152
  # GENERIC FUNCTIONS
173
153
  def firstrun
174
- puts @help
154
+ puts @info
175
155
  puts "Since there is no rsh configuration file (.rshrc), I will help you set it up to suit your needs.\n\n"
176
156
  puts "The prompt you see now is the very basic rsh prompt:"
177
157
  print "#{@prompt} (press ENTER)"
@@ -256,6 +236,12 @@ def getstr # A custom Readline-like function
256
236
  @pos = 0
257
237
  chr = ""
258
238
  @history.unshift("")
239
+ # Check if we have an AI suggestion to pre-fill
240
+ if @ai_suggestion
241
+ @history[0] = @ai_suggestion
242
+ @pos = @ai_suggestion.length
243
+ @ai_suggestion = nil
244
+ end
259
245
  @row0, p = @c.pos
260
246
  while chr != "ENTER" # Keep going with readline until user presses ENTER
261
247
  @ci = nil
@@ -569,6 +555,14 @@ def hist_clean # Clean up @history
569
555
  end
570
556
  def cmd_check(str) # Check if each element on the readline matches commands, nicks, paths; color them
571
557
  return if str.nil?
558
+
559
+ # Special handling for @ and @@ commands
560
+ if str =~ /^(@@?)\s+(.*)$/
561
+ prefix = $1
562
+ rest = $2
563
+ return prefix.c(4) + " " + rest # Color @ or @@ in blue (4), rest uncolored
564
+ end
565
+
572
566
  str.gsub(/(?:\S'[^']*'|[^ '])+/) do |el|
573
567
  if @exe.include?(el)
574
568
  el.c(@c_cmd)
@@ -610,7 +604,97 @@ end
610
604
 
611
605
  # RSH FUNCTIONS
612
606
  def help
613
- puts @help
607
+ # Get terminal width
608
+ term_width = @maxcol || 80
609
+ col_width = 48 # Fixed width for left column
610
+
611
+ # Helper function to strip ANSI codes for length calculation
612
+ def strip_ansi(str)
613
+ str.gsub(/\001?\e\[[0-9;]*m\002?/, '')
614
+ end
615
+
616
+ left_col = []
617
+ right_col = []
618
+
619
+ # Left column content
620
+ left_col << "KEYBOARD SHORTCUTS:".c(@c_prompt).b
621
+ left_col << "RIGHT/Ctrl-F Accept suggestion"
622
+ left_col << "UP/DOWN Navigate history"
623
+ left_col << "TAB Tab complete"
624
+ left_col << "Ctrl-Y Copy to clipboard"
625
+ left_col << "Ctrl-D Exit + save .rshrc"
626
+ left_col << "Ctrl-E Exit without save"
627
+ left_col << "Ctrl-L Clear screen"
628
+ left_col << "Ctrl-Z Suspend job"
629
+ left_col << "Ctrl-C/G Clear line"
630
+ left_col << "Ctrl-K Delete history item"
631
+ left_col << "Ctrl-U Clear line"
632
+ left_col << "Ctrl-W Delete previous word"
633
+ left_col << ""
634
+ left_col << "SPECIAL COMMANDS:".c(@c_prompt).b
635
+ left_col << ":nick 'll = ls -l' Command alias"
636
+ left_col << ":gnick 'h = /home' General alias"
637
+ left_col << ":nickdel 'name' Delete nick"
638
+ left_col << ":gnickdel 'name' Delete gnick"
639
+ left_col << ":nick? List all nicks"
640
+ left_col << ":history Show history"
641
+ left_col << ":rmhistory Clear history"
642
+ left_col << ":info About rsh"
643
+ left_col << ":version Version info"
644
+ left_col << ":help This help"
645
+
646
+ # Right column content
647
+ right_col << "RUBY FUNCTIONS:".c(@c_prompt).b
648
+ right_col << ":defun 'f(x) = x*2' Define function"
649
+ right_col << ":defun? List functions"
650
+ right_col << ":defun '-f' Remove function"
651
+ right_col << "Call as: f 5 (returns 10)"
652
+ right_col << ""
653
+ right_col << "JOB CONTROL:".c(@c_prompt).b
654
+ right_col << "command & Background job"
655
+ right_col << ":jobs List jobs"
656
+ right_col << ":fg [id] Foreground job"
657
+ right_col << ":bg [id] Resume in bg"
658
+ right_col << ""
659
+ right_col << "INTEGRATIONS:".c(@c_prompt).b
660
+ right_col << "r Launch rtfm"
661
+ right_col << "f Launch fzf"
662
+ right_col << "= <expr> xrpn calculator"
663
+ right_col << ":<ruby code> Execute Ruby"
664
+ right_col << ""
665
+ right_col << "AI FEATURES:".c(@c_prompt).b
666
+ right_col << "@ <question> AI text response"
667
+ right_col << "@@ <request> AI command → prompt"
668
+ right_col << ""
669
+ right_col << "EXPANSIONS:".c(@c_prompt).b
670
+ right_col << "~ Home directory"
671
+ right_col << "$VAR, ${VAR} Environment var"
672
+ right_col << "$? Exit status"
673
+ right_col << "$(cmd), `cmd` Command subst"
674
+ right_col << "{a,b,c} Brace expansion"
675
+ right_col << "cmd1 && cmd2 Conditional"
676
+ right_col << "cmd1 || cmd2 Alternative"
677
+
678
+ # Pad columns to same length
679
+ max_lines = [left_col.length, right_col.length].max
680
+ left_col.fill("", left_col.length...max_lines)
681
+ right_col.fill("", right_col.length...max_lines)
682
+
683
+ # Print in two columns
684
+ puts
685
+ max_lines.times do |i|
686
+ left_text = left_col[i].to_s
687
+ right_text = right_col[i].to_s
688
+ # Calculate padding based on visible characters (without ANSI codes)
689
+ visible_length = strip_ansi(left_text).length
690
+ padding = col_width - visible_length
691
+ padding = 0 if padding < 0
692
+ puts " #{left_text}#{' ' * padding} #{right_text}"
693
+ end
694
+ puts
695
+ end
696
+ def info
697
+ puts @info
614
698
  end
615
699
  def version
616
700
  puts "rsh version = #{@version} (latest RubyGems version is #{Gem.latest_version_for("ruby-shell").version} - https://github.com/isene/rsh)"
@@ -651,6 +735,16 @@ def nick? # Show nicks
651
735
  puts " General nicks:".c(@c_gnick)
652
736
  @gnick.sort.each {|key, value| puts " #{key} = #{value}"}
653
737
  end
738
+ def nickdel(nick_name) # Delete a command nick
739
+ @nick.delete(nick_name)
740
+ rshrc
741
+ puts "Nick '#{nick_name}' deleted"
742
+ end
743
+ def gnickdel(nick_name) # Delete a general/global nick
744
+ @gnick.delete(nick_name)
745
+ rshrc
746
+ puts "General nick '#{nick_name}' deleted"
747
+ end
654
748
  def dirs
655
749
  puts "Past direactories:"
656
750
  @dirs.each_with_index do |e,i|
@@ -785,6 +879,226 @@ def expand_braces(str)
785
879
  end
786
880
  end
787
881
 
882
+ # AI INTEGRATION FUNCTIONS
883
+ def get_ollama_model
884
+ begin
885
+ # Try to get list of available models
886
+ output = `ollama list 2>/dev/null`
887
+ return nil if output.empty? || $?.exitstatus != 0
888
+
889
+ # Parse the output to find a suitable model
890
+ lines = output.split("\n")
891
+ return nil if lines.length < 2
892
+
893
+ # Skip header line and get first available model
894
+ model_line = lines[1]
895
+ return nil if model_line.nil?
896
+
897
+ # Extract model name (first column)
898
+ model_name = model_line.split(/\s+/)[0]
899
+ return model_name
900
+ rescue => e
901
+ return nil
902
+ end
903
+ end
904
+
905
+ def ai_query(prompt)
906
+ # Get AI model configuration
907
+ model = @aimodel || nil
908
+ key = @aikey || nil
909
+
910
+ if model.nil? || model.empty?
911
+ # Try ollama first
912
+ if File.exist?("/usr/local/bin/ollama") || File.exist?("/usr/bin/ollama") || system("command -v ollama >/dev/null 2>&1")
913
+ begin
914
+ require 'json'
915
+ require 'net/http'
916
+
917
+ # First, get available models
918
+ ollama_model = get_ollama_model()
919
+ return ai_setup_help unless ollama_model
920
+
921
+ uri = URI('http://localhost:11434/api/generate')
922
+ http = Net::HTTP.new(uri.host, uri.port)
923
+ http.read_timeout = 30
924
+
925
+ request = Net::HTTP::Post.new(uri)
926
+ request['Content-Type'] = 'application/json'
927
+ request.body = {
928
+ model: ollama_model,
929
+ prompt: prompt,
930
+ stream: false,
931
+ options: {
932
+ num_predict: 200
933
+ }
934
+ }.to_json
935
+
936
+ response = http.request(request)
937
+ if response.code == '200'
938
+ result = JSON.parse(response.body)
939
+ return result['response']
940
+ else
941
+ return ai_setup_help
942
+ end
943
+ rescue => e
944
+ return ai_setup_help
945
+ end
946
+ else
947
+ return ai_setup_help
948
+ end
949
+ else
950
+ # Use external model
951
+ if model =~ /^gpt/i && key
952
+ begin
953
+ require 'json'
954
+ require 'net/http'
955
+ uri = URI('https://api.openai.com/v1/chat/completions')
956
+ http = Net::HTTP.new(uri.host, uri.port)
957
+ http.use_ssl = true
958
+ http.read_timeout = 30
959
+
960
+ request = Net::HTTP::Post.new(uri)
961
+ request['Content-Type'] = 'application/json'
962
+ request['Authorization'] = "Bearer #{key}"
963
+ request.body = {
964
+ model: model,
965
+ messages: [{role: 'user', content: prompt}],
966
+ max_tokens: 200,
967
+ temperature: 0.7
968
+ }.to_json
969
+
970
+ response = http.request(request)
971
+ if response.code == '200'
972
+ result = JSON.parse(response.body)
973
+ return result['choices'][0]['message']['content']
974
+ else
975
+ return "Error: #{response.code} - #{response.body}"
976
+ end
977
+ rescue => e
978
+ return "Error connecting to OpenAI: #{e.message}"
979
+ end
980
+ else
981
+ return "Unsupported model: #{model}. Currently only ollama and OpenAI models are supported."
982
+ end
983
+ end
984
+ end
985
+
986
+ def ai_command_suggest(prompt)
987
+ # Get AI model configuration
988
+ model = @aimodel || nil
989
+ key = @aikey || nil
990
+
991
+ # Modify prompt to request command output
992
+ cmd_prompt = "You are a Linux/Unix command line expert. Given this request: '#{prompt}', output ONLY the exact shell command that would accomplish this task. Output just the command itself with no explanation, no backticks, no markdown. For example, if asked 'list files' you would output: ls"
993
+
994
+ if model.nil? || model.empty?
995
+ # Try ollama first
996
+ if File.exist?("/usr/local/bin/ollama") || File.exist?("/usr/bin/ollama") || system("command -v ollama >/dev/null 2>&1")
997
+ begin
998
+ require 'json'
999
+ require 'net/http'
1000
+
1001
+ # First, get available models
1002
+ ollama_model = get_ollama_model()
1003
+ unless ollama_model
1004
+ puts ai_setup_help
1005
+ return nil
1006
+ end
1007
+
1008
+ uri = URI('http://localhost:11434/api/generate')
1009
+ http = Net::HTTP.new(uri.host, uri.port)
1010
+ http.read_timeout = 30
1011
+
1012
+ request = Net::HTTP::Post.new(uri)
1013
+ request['Content-Type'] = 'application/json'
1014
+ request.body = {
1015
+ model: ollama_model,
1016
+ prompt: cmd_prompt,
1017
+ stream: false,
1018
+ options: {
1019
+ num_predict: 50,
1020
+ temperature: 0.3
1021
+ }
1022
+ }.to_json
1023
+
1024
+ response = http.request(request)
1025
+ if response.code == '200'
1026
+ result = JSON.parse(response.body)
1027
+ cmd = result['response'].strip.split("\n")[0]
1028
+ return cmd
1029
+ else
1030
+ puts ai_setup_help
1031
+ return nil
1032
+ end
1033
+ rescue => e
1034
+ puts ai_setup_help
1035
+ return nil
1036
+ end
1037
+ else
1038
+ puts ai_setup_help
1039
+ return nil
1040
+ end
1041
+ else
1042
+ # Use external model
1043
+ if model =~ /^gpt/i && key
1044
+ begin
1045
+ require 'json'
1046
+ require 'net/http'
1047
+ uri = URI('https://api.openai.com/v1/chat/completions')
1048
+ http = Net::HTTP.new(uri.host, uri.port)
1049
+ http.use_ssl = true
1050
+ http.read_timeout = 30
1051
+
1052
+ request = Net::HTTP::Post.new(uri)
1053
+ request['Content-Type'] = 'application/json'
1054
+ request['Authorization'] = "Bearer #{key}"
1055
+ request.body = {
1056
+ model: model,
1057
+ messages: [{role: 'user', content: cmd_prompt}],
1058
+ max_tokens: 50,
1059
+ temperature: 0.3
1060
+ }.to_json
1061
+
1062
+ response = http.request(request)
1063
+ if response.code == '200'
1064
+ result = JSON.parse(response.body)
1065
+ cmd = result['choices'][0]['message']['content'].strip.split("\n")[0]
1066
+ return cmd
1067
+ else
1068
+ puts "Error: #{response.code} - #{response.body}"
1069
+ return nil
1070
+ end
1071
+ rescue => e
1072
+ puts "Error connecting to OpenAI: #{e.message}"
1073
+ return nil
1074
+ end
1075
+ else
1076
+ puts "Unsupported model: #{model}. Currently only ollama and OpenAI models are supported."
1077
+ return nil
1078
+ end
1079
+ end
1080
+ end
1081
+
1082
+ def ai_setup_help
1083
+ help_text = <<~HELP
1084
+
1085
+ AI is not configured. To use AI features, you have two options:
1086
+
1087
+ 1. Install Ollama (recommended for local AI):
1088
+ curl -fsSL https://ollama.com/install.sh | sh
1089
+ ollama pull llama3.2 # or any model you prefer
1090
+
1091
+ 2. Configure external AI model in ~/.rshrc:
1092
+ @aimodel = "gpt-4"
1093
+ @aikey = "your-api-key-here"
1094
+
1095
+ Once configured:
1096
+ - Use @ for AI text responses: @ What is the GDP of Norway?
1097
+ - Use @@ for AI command suggestions: @@ list files sorted by size
1098
+ HELP
1099
+ return help_text.c(@c_path)
1100
+ end
1101
+
788
1102
  # INITIAL SETUP
789
1103
  begin # Load .rshrc and populate @history
790
1104
  trap "SIGINT" do end
@@ -861,6 +1175,10 @@ loop do
861
1175
  hi = @history[@cmd.sub(/^!(\d+)$/, '\1').to_i+1]
862
1176
  @cmd = hi if hi
863
1177
  end
1178
+ # Move cursor to end of line and print the full command before clearing
1179
+ @c.row(@row0)
1180
+ @c.clear_line
1181
+ print @prompt + @cmd
864
1182
  print "\n"; @c.clear_screen_down
865
1183
  if @cmd == "r" # Integration with rtfm (https://github.com/isene/RTFM)
866
1184
  t = Time.now
@@ -877,6 +1195,23 @@ loop do
877
1195
  @cmd.gsub!(" ", ",")
878
1196
  @cmd = "echo \"#{@cmd[1...]},prx,off\" | xrpn"
879
1197
  end
1198
+ # AI integration with @ and @@
1199
+ if @cmd =~ /^@@\s+(.+)/ # AI command suggestion
1200
+ prompt = $1
1201
+ response = ai_command_suggest(prompt)
1202
+ if response
1203
+ # Store the suggestion for the next prompt
1204
+ @ai_suggestion = response
1205
+ # Also add to history for record keeping
1206
+ @history.unshift(response)
1207
+ end
1208
+ next
1209
+ elsif @cmd =~ /^@\s+(.+)/ # AI text response
1210
+ prompt = $1
1211
+ response = ai_query(prompt)
1212
+ puts response if response
1213
+ next
1214
+ end
880
1215
  if @cmd.match(/^\s*:/) # Ruby commands are prefixed with ":"
881
1216
  begin
882
1217
  eval(@cmd[1..-1])
metadata CHANGED
@@ -1,20 +1,21 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-shell
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-01 00:00:00.000000000 Z
11
+ date: 2025-07-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
14
- history, syntax highlighting, theming, auto-cd, auto-opening files and more. MAJOR
15
- UPDATE v2.7.0: Ruby Functions - define custom shell commands using full Ruby power!
16
- Also: job control (background jobs, Ctrl-Z suspension), command substitution, variable
17
- expansion, conditional execution, login shell support, and much more.'
14
+ history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
15
+ v2.9.0: AI integration! Use @ for AI text responses and @@ for AI command suggestions.
16
+ Works with local Ollama or external providers like OpenAI. v2.8.0: Enhanced help
17
+ system, :info command, and easier nick management. v2.7.0: Ruby Functions, job control,
18
+ command substitution, and more.'
18
19
  email: g@isene.com
19
20
  executables:
20
21
  - rsh