ruby-shell 3.4.0 → 3.4.2
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 +94 -91
- data/bin/rsh +70 -8
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77bd4af1f0ec04f25fb48fc4bde5b1b18a50b02e046698c9cb660dafa6caa59c
|
|
4
|
+
data.tar.gz: 807220a097167fb1338c52ce8868d3fcf0a022d8cec7963cf23654daafd72e8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e84bedcc95db51816bcfd6c5ed560914898a3e661767bcd810c6dab22d8aec7fffb8904877a606e3cf9cdc1129307fd50c1db848f429165aa935d8a57f5b46d
|
|
7
|
+
data.tar.gz: 0d939b9668cd56581466acf659cc99b75652522ee5f05d5d6fdcf7eb70930da766a818dcbeef6d81597ce24e4dc9f720a6364bc5792ccad05231ba216e9fdcb2
|
data/README.md
CHANGED
|
@@ -19,97 +19,100 @@ Or simply `gem install ruby-shell`.
|
|
|
19
19
|
|
|
20
20
|
# Features
|
|
21
21
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* **
|
|
46
|
-
* **
|
|
47
|
-
* **
|
|
48
|
-
* **
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* **
|
|
52
|
-
* **
|
|
53
|
-
* **
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
* **
|
|
59
|
-
* **
|
|
60
|
-
* **
|
|
61
|
-
* **
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* **
|
|
65
|
-
* **
|
|
66
|
-
* **
|
|
67
|
-
* **
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
22
|
+
## Key Features
|
|
23
|
+
|
|
24
|
+
### Aliases (Nicks)
|
|
25
|
+
**rsh uses "nicks" for aliases** - both simple command shortcuts and powerful parametrized templates:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Simple aliases
|
|
29
|
+
:nick la = ls -la
|
|
30
|
+
:nick gs = git status
|
|
31
|
+
|
|
32
|
+
# Parametrized nicks (templates with {{placeholders}})
|
|
33
|
+
:nick gp = git push origin {{branch}}
|
|
34
|
+
gp branch=main # Executes: git push origin main
|
|
35
|
+
|
|
36
|
+
:nick deploy = ssh {{user}}@{{host}} 'systemctl restart {{app}}'
|
|
37
|
+
deploy user=admin host=prod app=api
|
|
38
|
+
|
|
39
|
+
# List and manage
|
|
40
|
+
:nick # List all nicks
|
|
41
|
+
:nick -la # Delete a nick
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Intelligence & Learning
|
|
45
|
+
* **Completion Learning**: Shell learns which TAB completions you use and ranks them higher
|
|
46
|
+
* **Smart Suggestions**: "Did you mean...?" for typos
|
|
47
|
+
* **Auto-correct**: Optional auto-fix with confirmation
|
|
48
|
+
* **Command Analytics**: `:stats` shows usage patterns and performance
|
|
49
|
+
|
|
50
|
+
### Productivity
|
|
51
|
+
* **Command Recording**: `:record start name` → run commands → `:record stop` → `:replay name`
|
|
52
|
+
* **Sessions**: Save/load entire shell state with bookmarks, history, and functions
|
|
53
|
+
* **Bookmarks**: Tag directories and jump instantly
|
|
54
|
+
* **Multi-line Editing**: Press Ctrl-G to edit in $EDITOR
|
|
55
|
+
* **Shell Scripts**: Full bash support for for/while/if loops
|
|
56
|
+
|
|
57
|
+
### Extensibility
|
|
58
|
+
* **Plugin System**: Add custom commands, completions, and hooks
|
|
59
|
+
* **Ruby Functions**: Define callable functions - `:defun hello(name) = puts "Hello, #{name}!"`
|
|
60
|
+
* **Validation Rules**: `:validate rm -rf / = block` prevents dangerous commands
|
|
61
|
+
* **6 Color Themes**: solarized, dracula, gruvbox, nord, monokai, default
|
|
62
|
+
|
|
63
|
+
### Integrations
|
|
64
|
+
* **AI Support**: @ for questions, @@ for command suggestions (Ollama or OpenAI)
|
|
65
|
+
* **RTFM**: Launch file manager with `r`
|
|
66
|
+
* **fzf**: Fuzzy finder with `f`
|
|
67
|
+
* **XRPN**: Calculator with `= expression`
|
|
68
|
+
|
|
69
|
+
### Tab Completion
|
|
70
|
+
* Smart context-aware completion for git, apt, docker, systemctl, cargo, npm, gem
|
|
71
|
+
* Command switches from --help
|
|
72
|
+
* Option values (--format=json, --level=debug)
|
|
73
|
+
* Learns your patterns and adapts
|
|
74
|
+
|
|
75
|
+
### Core Shell
|
|
76
|
+
* Syntax highlighting for nicks, commands, paths, bookmarks
|
|
77
|
+
* History with search, edit, and repeat (!, !!, !-2, !5:7)
|
|
78
|
+
* Job control (background jobs, suspend, resume)
|
|
79
|
+
* Config file (.rshrc) updates on exit
|
|
80
|
+
* All colors themeable
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Install
|
|
88
|
+
gem install ruby-shell
|
|
89
|
+
|
|
90
|
+
# Run
|
|
91
|
+
rsh
|
|
92
|
+
|
|
93
|
+
# Create an alias
|
|
94
|
+
:nick ll = ls -l
|
|
95
|
+
ll
|
|
96
|
+
|
|
97
|
+
# Create parametrized alias
|
|
98
|
+
:nick gp = git push origin {{branch}}
|
|
99
|
+
gp branch=main
|
|
100
|
+
|
|
101
|
+
# Get help
|
|
102
|
+
:help
|
|
103
|
+
:info
|
|
104
|
+
|
|
105
|
+
# See version and changelog
|
|
106
|
+
:version
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Latest Features (v3.4)
|
|
109
112
|
* **Define Ruby functions as shell commands**: `:defun 'weather(*args) = system("curl -s wttr.in/#{args[0] || \"oslo\"}")'`
|
|
110
113
|
* **Call like any shell command**: `weather london`
|
|
111
114
|
* **Full Ruby power**: Access to Ruby stdlib, file operations, JSON parsing, web requests, etc.
|
|
112
|
-
* **Function management**: `:defun
|
|
115
|
+
* **Function management**: `:defun` to list, `:defun -name` to remove
|
|
113
116
|
* **Syntax highlighting**: Ruby functions highlighted in bold
|
|
114
117
|
|
|
115
118
|
## Advanced Shell Features
|
|
@@ -135,7 +138,7 @@ Special commands:
|
|
|
135
138
|
* `:history` will list the command history, while `:rmhistory` will delete the history
|
|
136
139
|
* `:jobs` will list background jobs, `:fg [job_id]` brings jobs to foreground, `:bg [job_id]` resumes stopped jobs
|
|
137
140
|
* `:defun func(args) = code` defines Ruby functions callable as shell commands (persistent!)
|
|
138
|
-
* `:defun
|
|
141
|
+
* `:defun` lists all user-defined functions, `:defun -func` removes functions
|
|
139
142
|
* `:stats` shows command execution statistics, `:stats --graph` for visual charts, `:stats --clear` to reset
|
|
140
143
|
* `:bm name` or `:bookmark name` bookmark current directory, `:bm name path #tags` with tags
|
|
141
144
|
* `:bm` lists all bookmarks, just type bookmark name to jump (e.g., `work`)
|
|
@@ -306,8 +309,8 @@ weather london
|
|
|
306
309
|
|
|
307
310
|
### Function Management
|
|
308
311
|
```bash
|
|
309
|
-
:defun
|
|
310
|
-
:defun
|
|
312
|
+
:defun # List all defined functions
|
|
313
|
+
:defun -myls # Remove a function
|
|
311
314
|
```
|
|
312
315
|
|
|
313
316
|
Ruby functions have access to:
|
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 = "3.4.
|
|
11
|
+
@version = "3.4.2" # Improved defun syntax: :defun lists, :defun -name removes, enhanced :help
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -155,6 +155,8 @@ begin # Initialization
|
|
|
155
155
|
@completion_learning = true # Enable completion learning (default: on)
|
|
156
156
|
@recording = {active: false, name: nil, commands: []} # Command recording state
|
|
157
157
|
@recordings = {} # Saved recordings
|
|
158
|
+
@command_cache = {} # Cache for expensive shell command outputs
|
|
159
|
+
@last_prompt_dir = nil # Track directory for .rshrc reload optimization
|
|
158
160
|
# Built-in rsh commands are called with : prefix, so no need for separate tracking
|
|
159
161
|
Dir.mkdir(Dir.home + '/.rsh') unless Dir.exist?(Dir.home + '/.rsh')
|
|
160
162
|
Dir.mkdir(@session_dir) unless Dir.exist?(@session_dir)
|
|
@@ -1157,6 +1159,15 @@ def rshrc # Write updates to .rshrc
|
|
|
1157
1159
|
conf += "@completion_learning = #{@completion_learning}\n" unless @completion_learning
|
|
1158
1160
|
conf.sub!(/^@recordings.*(\n|$)/, "")
|
|
1159
1161
|
conf += "@recordings = #{@recordings}\n" unless @recordings.empty?
|
|
1162
|
+
# Persist executable cache for faster startup
|
|
1163
|
+
if @exe && @exe.length > 100
|
|
1164
|
+
conf.sub!(/^@exe_cache.*(\n|$)/, "")
|
|
1165
|
+
conf += "@exe_cache = #{@exe.inspect}\n"
|
|
1166
|
+
conf.sub!(/^@exe_cache_path.*(\n|$)/, "")
|
|
1167
|
+
conf += "@exe_cache_path = #{ENV['PATH'].inspect}\n"
|
|
1168
|
+
conf.sub!(/^@exe_cache_time.*(\n|$)/, "")
|
|
1169
|
+
conf += "@exe_cache_time = #{Time.now.to_i}\n"
|
|
1170
|
+
end
|
|
1160
1171
|
# Only write @cmd_completions if user has customized it
|
|
1161
1172
|
unless conf =~ /^@cmd_completions\s*=/
|
|
1162
1173
|
# Don't write default completions to avoid cluttering .rshrc
|
|
@@ -1199,10 +1210,13 @@ def help
|
|
|
1199
1210
|
col1 << "Shift-TAB History search"
|
|
1200
1211
|
col1 << ""
|
|
1201
1212
|
col1 << "CORE COMMANDS:".c(@c_prompt).b
|
|
1202
|
-
col1 << ":nick a = b
|
|
1203
|
-
col1 << ":nick
|
|
1213
|
+
col1 << ":nick a = b Create alias"
|
|
1214
|
+
col1 << ":nick List all"
|
|
1215
|
+
col1 << ":nick -a Delete"
|
|
1216
|
+
col1 << ":defun f()=x Create function"
|
|
1217
|
+
col1 << ":defun List all"
|
|
1218
|
+
col1 << ":defun -f Delete"
|
|
1204
1219
|
col1 << ":bm name Bookmark"
|
|
1205
|
-
col1 << ":defun f()=x Function"
|
|
1206
1220
|
col1 << ":stats Analytics"
|
|
1207
1221
|
col1 << ":validate p=a Safety rules"
|
|
1208
1222
|
col1 << ":calc expr Calculator"
|
|
@@ -1404,10 +1418,23 @@ def bg(job_id = nil)
|
|
|
1404
1418
|
puts "Job #{job_id} no longer exists"
|
|
1405
1419
|
end
|
|
1406
1420
|
end
|
|
1407
|
-
def defun(func_def) # Define a Ruby function like: `:defun
|
|
1408
|
-
if func_def.
|
|
1421
|
+
def defun(func_def = nil) # Define a Ruby function like: `:defun myls(*args) = Dir.glob('*').each {|f| puts f}`
|
|
1422
|
+
if func_def.nil? || func_def.strip.empty?
|
|
1423
|
+
# List all defined functions
|
|
1424
|
+
puts "User-defined Ruby functions:"
|
|
1425
|
+
all_methods = singleton_class.instance_methods(false)
|
|
1426
|
+
excluded = [:defun, :defun?, :execute_conditional, :expand_braces]
|
|
1427
|
+
methods = all_methods - excluded
|
|
1428
|
+
if methods.empty?
|
|
1429
|
+
puts " (none defined)"
|
|
1430
|
+
else
|
|
1431
|
+
methods.each do |method|
|
|
1432
|
+
puts " #{method}"
|
|
1433
|
+
end
|
|
1434
|
+
end
|
|
1435
|
+
elsif func_def.match(/^\s*-/)
|
|
1409
1436
|
# Remove function
|
|
1410
|
-
func_name = func_def.sub(/^\s*-/, '')
|
|
1437
|
+
func_name = func_def.sub(/^\s*-/, '').strip
|
|
1411
1438
|
if self.respond_to?(func_name)
|
|
1412
1439
|
singleton_class.remove_method(func_name.to_sym)
|
|
1413
1440
|
@defuns.delete(func_name)
|
|
@@ -2862,10 +2889,40 @@ def load_defaults
|
|
|
2862
2889
|
puts "Loaded with default configuration."
|
|
2863
2890
|
end
|
|
2864
2891
|
|
|
2892
|
+
def cached_command(cmd, ttl = 300) # Cache expensive command outputs
|
|
2893
|
+
key = cmd.hash
|
|
2894
|
+
now = Time.now.to_i
|
|
2895
|
+
|
|
2896
|
+
if @command_cache[key] && (now - @command_cache[key][:time]) < ttl
|
|
2897
|
+
return @command_cache[key][:result]
|
|
2898
|
+
end
|
|
2899
|
+
|
|
2900
|
+
result = `#{cmd}`.chomp
|
|
2901
|
+
@command_cache[key] = {result: result, time: now}
|
|
2902
|
+
|
|
2903
|
+
# Limit cache size
|
|
2904
|
+
if @command_cache.length > 50
|
|
2905
|
+
# Remove oldest entry
|
|
2906
|
+
oldest = @command_cache.min_by { |k, v| v[:time] }
|
|
2907
|
+
@command_cache.delete(oldest[0])
|
|
2908
|
+
end
|
|
2909
|
+
|
|
2910
|
+
result
|
|
2911
|
+
end
|
|
2865
2912
|
def cache_executables
|
|
2866
2913
|
current_path = ENV["PATH"]
|
|
2867
2914
|
current_time = Time.now.to_i
|
|
2868
2915
|
|
|
2916
|
+
# Use persisted cache if valid (from .rshrc)
|
|
2917
|
+
if @exe_cache && @exe_cache_path == current_path
|
|
2918
|
+
cache_age = current_time - (@exe_cache_time || 0)
|
|
2919
|
+
if cache_age < 3600 # 1 hour
|
|
2920
|
+
@exe = @exe_cache
|
|
2921
|
+
@exe_cache_paths = current_path
|
|
2922
|
+
return
|
|
2923
|
+
end
|
|
2924
|
+
end
|
|
2925
|
+
|
|
2869
2926
|
# Only rebuild cache if PATH changed or cache is older than 60 seconds
|
|
2870
2927
|
return if @exe_cache_paths == current_path && (current_time - @exe_cache_time) < 60
|
|
2871
2928
|
|
|
@@ -2940,7 +2997,12 @@ loop do
|
|
|
2940
2997
|
begin
|
|
2941
2998
|
@user = Etc.getpwuid(Process.euid).name # For use in @prompt
|
|
2942
2999
|
@node = Etc.uname[:nodename] # For use in @prompt
|
|
2943
|
-
|
|
3000
|
+
# Only reload .rshrc if directory changed (optimization)
|
|
3001
|
+
current_dir = Dir.pwd
|
|
3002
|
+
if @last_prompt_dir != current_dir
|
|
3003
|
+
h = @history; f = @cmd_frequency; s = @cmd_stats; b = @bookmarks; d = @defuns; load_rshrc_safe; @history = h; @cmd_frequency = f; @cmd_stats = s; @bookmarks = b; @defuns = d
|
|
3004
|
+
@last_prompt_dir = current_dir
|
|
3005
|
+
end
|
|
2944
3006
|
@prompt.gsub!(/#{Dir.home}/, '~') # Simplify path in prompt
|
|
2945
3007
|
system("printf \"\033]0;rsh: #{Dir.pwd}\007\"") # Set Window title to path
|
|
2946
3008
|
@history[0] = "" unless @history[0]
|