rtfm-filemanager 8.2.6 → 8.5.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/CHANGELOG.md +13 -0
- data/bin/rtfm +169 -42
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f884960bd43345ca3368b1213be174efecf43943d20be674d722b70b848a7edf
|
|
4
|
+
data.tar.gz: 5c3b0d4c294ee89015df17b5b5adc4ba78b7080fee34fa5825d1a7a88f92531c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef00e7d08a4b579bb5a989cdbac6c30d02a77462401faeddaf30de42a464ccbbb547dcca7f369d050f0e64b83a8be8e4930eb38500d548bd010f4b77815f72c3
|
|
7
|
+
data.tar.gz: c063661025b9887def4c49f146042a78cc6dd3cf0dbc7fd5b42b929f3219fc64e5e79412bb2a6afa9cc2cfaa3188cf7f2f5380102573ecd4cd5ef0f07bd56544
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to RTFM will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.5.0] - 2026-05-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Crash log** - Unhandled exceptions now append to `~/.rtfm/crash.log` with timestamp, version, Ruby/platform info, cwd, exception class/message, and full backtrace. The `at_exit` handler skips `SystemExit`/`Interrupt` so normal quits stay silent. Makes bug reports actionable instead of guesswork from terminal scrollback
|
|
12
|
+
- **Persistent per-directory cursor** - The in-memory `@directory` hash that tracks selected index per directory is now saved to `~/.rtfm/conf` on quit (capped at 200 entries, prunes deleted dirs). Re-enter a directory in a later session and the cursor lands where you left it
|
|
13
|
+
- **`--fresh` CLI flag** - Launch with `rtfm --fresh` to ignore the saved cursor map for one session (useful when the saved positions are stale or you want a clean slate)
|
|
14
|
+
- **File-path argument** - `rtfm /etc/hosts` now cd's into `/etc` and places the cursor on `hosts`. Previously only directory arguments worked
|
|
15
|
+
- **Claude (Anthropic) support for AI features** - Set `@aimodel = "claude-sonnet-4-6"` (or any `claude-*` model) in `~/.rtfm/conf` and both `I` (file description) and `Ctrl-a` (chat) route to the Anthropic API via curl. OpenAI continues to work for non-`claude-*` model names. Same `@ai` config field holds either key
|
|
16
|
+
- **Non-blocking `:` commands** - Non-interactive shell commands now run in a background thread. Launching GUI apps (`xdg-open`, `firefox`, `evince`, etc.) no longer freezes RTFM until they exit. Output drops into the right pane when the command completes. The bottom pane shows running/done status
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Bootsnap optional** - Missing `bootsnap` gem no longer crashes RTFM on startup. It was always intended as a startup-speed optimization; now `require 'bootsnap/setup'` is wrapped in a `LoadError` rescue and RTFM continues without it
|
|
20
|
+
|
|
8
21
|
## [8.2.6] - 2026-04-21
|
|
9
22
|
|
|
10
23
|
### Added
|
data/bin/rtfm
CHANGED
|
@@ -18,21 +18,46 @@
|
|
|
18
18
|
# get a great understanding of the code itself by simply sending
|
|
19
19
|
# or pasting this whole file into you favorite AI for coding with
|
|
20
20
|
# a prompt like this: "Help me understand every part of this code".
|
|
21
|
-
@version = '8.
|
|
21
|
+
@version = '8.5.0' # Crash log, persistent per-dir cursor, Claude AI, non-blocking `:` commands
|
|
22
22
|
|
|
23
23
|
# SAVE & STORE TERMINAL {{{1
|
|
24
24
|
ORIG_STTY = `stty -g`.chomp
|
|
25
25
|
|
|
26
26
|
at_exit do
|
|
27
|
+
# Log unhandled exceptions to ~/.rtfm/crash.log so users can file bug reports
|
|
28
|
+
# without losing the backtrace to terminal scrollback.
|
|
29
|
+
if $! && !$!.is_a?(SystemExit) && !$!.is_a?(Interrupt)
|
|
30
|
+
begin
|
|
31
|
+
log = File.join(Dir.home, '.rtfm', 'crash.log')
|
|
32
|
+
require 'fileutils'
|
|
33
|
+
FileUtils.mkdir_p(File.dirname(log))
|
|
34
|
+
File.open(log, 'a') do |f|
|
|
35
|
+
f.puts '=' * 60
|
|
36
|
+
f.puts "RTFM v#{@version} crash at #{Time.now}"
|
|
37
|
+
f.puts "Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
|
|
38
|
+
f.puts "PWD: #{Dir.pwd rescue 'unknown'}"
|
|
39
|
+
f.puts "#{$!.class}: #{$!.message}"
|
|
40
|
+
f.puts($!.backtrace.join("\n")) if $!.backtrace
|
|
41
|
+
end
|
|
42
|
+
rescue StandardError
|
|
43
|
+
# never crash inside the crash handler
|
|
44
|
+
end
|
|
45
|
+
end
|
|
27
46
|
system("stty #{ORIG_STTY} < /dev/tty") rescue nil
|
|
28
47
|
end
|
|
29
48
|
|
|
30
49
|
# BOOTSNAP {{{1
|
|
50
|
+
# Optional startup speedup. If the gem isn't installed, RTFM just starts
|
|
51
|
+
# a bit slower instead of refusing to run.
|
|
31
52
|
cache_dir = ENV.fetch('BOOTSNAP_CACHE_DIR', File.join(Dir.home, '.rtfm', 'bootsnap-cache'))
|
|
32
53
|
ENV['BOOTSNAP_CACHE_DIR'] = cache_dir
|
|
33
54
|
require 'fileutils'
|
|
34
55
|
FileUtils.mkdir_p(cache_dir)
|
|
35
|
-
|
|
56
|
+
begin
|
|
57
|
+
require 'bootsnap/setup'
|
|
58
|
+
rescue LoadError
|
|
59
|
+
# bootsnap not installed — proceed without compile cache
|
|
60
|
+
end
|
|
36
61
|
|
|
37
62
|
# ENCODING {{{1
|
|
38
63
|
# encoding: utf-8
|
|
@@ -616,6 +641,13 @@ $stdin.set_encoding(Encoding::UTF_8)
|
|
|
616
641
|
@file_op_complete = false # Flag when operation finishes
|
|
617
642
|
@file_op_result = nil # Result message when operation finishes
|
|
618
643
|
|
|
644
|
+
## Async shell command (`:` mode, non-interactive branch)
|
|
645
|
+
@shell_cmd_thread = nil # Current background shell-command thread
|
|
646
|
+
@shell_cmd_running = false # True while a background `:` cmd is running
|
|
647
|
+
@shell_cmd_label = nil # Command string for status display
|
|
648
|
+
@shell_cmd_complete = false # Set by worker when output is ready
|
|
649
|
+
@shell_cmd_output = nil # Captured stdout+stderr
|
|
650
|
+
|
|
619
651
|
## Recently accessed files/directories
|
|
620
652
|
@recent_files = [] # Last 50 accessed files
|
|
621
653
|
@recent_dirs = [] # Last 20 accessed directories
|
|
@@ -860,11 +892,33 @@ if (pick_arg = ARGV.find { |a| a.start_with?('--pick=') })
|
|
|
860
892
|
ARGV.delete(pick_arg)
|
|
861
893
|
end
|
|
862
894
|
|
|
895
|
+
# --fresh: ignore any saved per-directory cursor positions for this session.
|
|
896
|
+
# @directory is still tracked in-memory, but startup restoration is skipped.
|
|
897
|
+
@fresh = false
|
|
898
|
+
if ARGV.delete('--fresh')
|
|
899
|
+
@fresh = true
|
|
900
|
+
@directory = {}
|
|
901
|
+
end
|
|
902
|
+
|
|
863
903
|
# Handle start dir {{{2
|
|
864
|
-
|
|
904
|
+
# Accept either a directory or a file path. With a file path, cd into its
|
|
905
|
+
# parent and queue the basename for cursor restoration after load_dir runs.
|
|
906
|
+
@startup_select = nil
|
|
907
|
+
if (arg = ARGV[0]) && !arg.start_with?('-')
|
|
908
|
+
if File.directory?(arg)
|
|
909
|
+
Dir.chdir(ARGV.shift)
|
|
910
|
+
elsif File.exist?(arg)
|
|
911
|
+
@startup_select = File.basename(arg)
|
|
912
|
+
Dir.chdir(File.dirname(File.expand_path(arg)))
|
|
913
|
+
ARGV.shift
|
|
914
|
+
end
|
|
915
|
+
end
|
|
865
916
|
|
|
866
917
|
# Initialize first tab {{{2
|
|
867
918
|
create_tab(Dir.pwd, "Main")
|
|
919
|
+
# Seed the first tab's per-tab directory_memory with the persisted @directory
|
|
920
|
+
# so switching tabs and back doesn't lose the saved cursor map.
|
|
921
|
+
@tabs[0][:directory_memory] = @directory.dup if @tabs[0]
|
|
868
922
|
|
|
869
923
|
# OPENAI SETUP {{{1
|
|
870
924
|
def chat_history # {{{2
|
|
@@ -880,6 +934,33 @@ def openai_client # {{{2
|
|
|
880
934
|
@openai_client ||= OpenAI::Client.new(access_token: @ai)
|
|
881
935
|
end
|
|
882
936
|
|
|
937
|
+
# AI dispatch: routes to Anthropic when @aimodel starts with 'claude-',
|
|
938
|
+
# otherwise OpenAI. Returns the assistant message text, or nil on failure.
|
|
939
|
+
# Anthropic uses curl (no extra gem dependency); OpenAI uses ruby-openai.
|
|
940
|
+
def ai_request(messages, max_tokens = 600) # {{{2
|
|
941
|
+
if @aimodel.to_s.start_with?('claude-')
|
|
942
|
+
require 'json'
|
|
943
|
+
# Anthropic expects "system" as a top-level field, not a message role.
|
|
944
|
+
sys = messages.find { |m| (m[:role] || m['role']).to_s == 'system' }
|
|
945
|
+
msgs = messages.reject { |m| (m[:role] || m['role']).to_s == 'system' }
|
|
946
|
+
body = { model: @aimodel, max_tokens: max_tokens, messages: msgs }
|
|
947
|
+
body[:system] = sys[:content] || sys['content'] if sys
|
|
948
|
+
out = `curl -sS -X POST https://api.anthropic.com/v1/messages \
|
|
949
|
+
-H 'content-type: application/json' \
|
|
950
|
+
-H 'anthropic-version: 2023-06-01' \
|
|
951
|
+
-H #{Shellwords.escape("x-api-key: #{@ai}")} \
|
|
952
|
+
-d #{Shellwords.escape(body.to_json)} 2>/dev/null`
|
|
953
|
+
return nil if out.empty?
|
|
954
|
+
json = JSON.parse(out) rescue nil
|
|
955
|
+
json && json.dig('content', 0, 'text')
|
|
956
|
+
else
|
|
957
|
+
resp = openai_client.chat(
|
|
958
|
+
parameters: { model: @aimodel, messages: messages, max_tokens: max_tokens }
|
|
959
|
+
) rescue nil
|
|
960
|
+
resp&.dig('choices', 0, 'message', 'content')
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
883
964
|
# SET UP VIEWER SYSTEM {{{1
|
|
884
965
|
# rubocop:disable Style/StringLiterals
|
|
885
966
|
preview_specs = {
|
|
@@ -4804,16 +4885,19 @@ end
|
|
|
4804
4885
|
|
|
4805
4886
|
def openai_description # {{{3
|
|
4806
4887
|
clear_image
|
|
4807
|
-
begin
|
|
4808
|
-
require 'ruby/openai'
|
|
4809
|
-
rescue LoadError
|
|
4810
|
-
@pB.say('To enable AI-descriptions: `gem install ruby-openai` and set @ai in ~/.rtfm/conf')
|
|
4811
|
-
return
|
|
4812
|
-
end
|
|
4813
4888
|
unless @ai && !@ai.empty?
|
|
4814
|
-
@pB.say("Set your API key in ~/.rtfm/conf: @ai = 'your-
|
|
4889
|
+
@pB.say("Set your API key in ~/.rtfm/conf: @ai = 'your-api-key'")
|
|
4815
4890
|
return
|
|
4816
4891
|
end
|
|
4892
|
+
# ruby-openai is only needed for OpenAI models; Anthropic uses curl.
|
|
4893
|
+
unless @aimodel.to_s.start_with?('claude-')
|
|
4894
|
+
begin
|
|
4895
|
+
require 'ruby/openai'
|
|
4896
|
+
rescue LoadError
|
|
4897
|
+
@pB.say('For OpenAI models: `gem install ruby-openai`. For Claude, set @aimodel to a claude-* model.')
|
|
4898
|
+
return
|
|
4899
|
+
end
|
|
4900
|
+
end
|
|
4817
4901
|
# Context
|
|
4818
4902
|
path = File.join(Dir.pwd, @selected.to_s)
|
|
4819
4903
|
is_dir = File.directory?(path)
|
|
@@ -4827,27 +4911,26 @@ def openai_description # {{{3
|
|
|
4827
4911
|
parts << "Git-Aware Diff Explanation: summarize the most recent `git diff` touching #{path}, explaining what changed." if Dir.exist?(File.join(Dir.pwd, '.git'))
|
|
4828
4912
|
parts << "Existing preview text: #{preview}" unless preview.empty?
|
|
4829
4913
|
prompt = parts.join(' ')
|
|
4830
|
-
# Send to OpenAI
|
|
4831
|
-
client = OpenAI::Client.new(access_token: @ai)
|
|
4832
4914
|
@pR.say('Thinking...'.fg(244))
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
model: @aimodel,
|
|
4836
|
-
messages: [{ role: 'user', content: prompt }],
|
|
4837
|
-
max_tokens: 600
|
|
4838
|
-
}
|
|
4839
|
-
) rescue nil
|
|
4840
|
-
answer = response&.dig('choices', 0, 'message', 'content') ||
|
|
4841
|
-
'⚠️ Error or empty response from OpenAI.'
|
|
4915
|
+
answer = ai_request([{ role: 'user', content: prompt }], 600) ||
|
|
4916
|
+
'⚠️ Error or empty response from AI provider.'
|
|
4842
4917
|
@pR.say(answer.fg(230))
|
|
4843
4918
|
end
|
|
4844
4919
|
|
|
4845
4920
|
def chat_mode # {{{3
|
|
4846
4921
|
clear_image
|
|
4847
|
-
unless
|
|
4848
|
-
@pB.say("
|
|
4922
|
+
unless @ai && !@ai.empty?
|
|
4923
|
+
@pB.say("Set your API key in ~/.rtfm/conf: @ai = 'your-api-key'")
|
|
4849
4924
|
return
|
|
4850
4925
|
end
|
|
4926
|
+
unless @aimodel.to_s.start_with?('claude-')
|
|
4927
|
+
begin
|
|
4928
|
+
require 'ruby/openai'
|
|
4929
|
+
rescue LoadError
|
|
4930
|
+
@pB.say('For OpenAI models: `gem install ruby-openai`. For Claude, set @aimodel to a claude-* model.')
|
|
4931
|
+
return
|
|
4932
|
+
end
|
|
4933
|
+
end
|
|
4851
4934
|
|
|
4852
4935
|
@pB.clear; @pB.update = true
|
|
4853
4936
|
question = @pAI.ask('Chat> ', '').strip
|
|
@@ -4855,15 +4938,7 @@ def chat_mode # {{{3
|
|
|
4855
4938
|
|
|
4856
4939
|
chat_history << { role: 'user', content: question }
|
|
4857
4940
|
@pR.say('Thinking...'.fg(230))
|
|
4858
|
-
|
|
4859
|
-
parameters: {
|
|
4860
|
-
model: @aimodel,
|
|
4861
|
-
messages: chat_history,
|
|
4862
|
-
max_tokens: 400
|
|
4863
|
-
}
|
|
4864
|
-
) rescue nil
|
|
4865
|
-
answer = reply&.dig('choices', 0, 'message', 'content') ||
|
|
4866
|
-
'⚠️ API error or empty response'
|
|
4941
|
+
answer = ai_request(chat_history, 400) || '⚠️ API error or empty response'
|
|
4867
4942
|
chat_history << { role: 'assistant', content: answer }
|
|
4868
4943
|
@pR.say(answer.fg(230))
|
|
4869
4944
|
@pB.clear; @pB.update = true
|
|
@@ -5385,15 +5460,33 @@ def command_mode # {{{3
|
|
|
5385
5460
|
refresh
|
|
5386
5461
|
render
|
|
5387
5462
|
else
|
|
5388
|
-
#
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
@
|
|
5463
|
+
# Run non-interactive commands in a background thread so GUI apps
|
|
5464
|
+
# (xdg-open, firefox, evince, …) and long-running tools don't freeze
|
|
5465
|
+
# the TUI. The main loop polls @shell_cmd_complete and drops output
|
|
5466
|
+
# into the right pane when ready.
|
|
5467
|
+
if @shell_cmd_running
|
|
5468
|
+
@pB.say("A background command is still running: #{@shell_cmd_label}".fg(214))
|
|
5469
|
+
return
|
|
5470
|
+
end
|
|
5471
|
+
@shell_cmd_running = true
|
|
5472
|
+
@shell_cmd_complete = false
|
|
5473
|
+
@shell_cmd_output = nil
|
|
5474
|
+
@shell_cmd_label = cmd
|
|
5475
|
+
cmd_str = cmd.dup
|
|
5476
|
+
@shell_cmd_thread = Thread.new do
|
|
5477
|
+
begin
|
|
5478
|
+
out, err, _status = Open3.capture3('sh', '-c', cmd_str)
|
|
5479
|
+
combined = out.dup
|
|
5480
|
+
combined << "\n" << err.fg(196) unless err.empty?
|
|
5481
|
+
@shell_cmd_output = combined
|
|
5482
|
+
rescue StandardError => e
|
|
5483
|
+
@shell_cmd_output = "Error: #{e.class}: #{e.message}".fg(196)
|
|
5484
|
+
ensure
|
|
5485
|
+
@shell_cmd_complete = true
|
|
5486
|
+
end
|
|
5487
|
+
end
|
|
5488
|
+
@pB.say(" Running: #{cmd}".fg(244))
|
|
5489
|
+
@pB.update = true
|
|
5397
5490
|
end
|
|
5398
5491
|
end
|
|
5399
5492
|
|
|
@@ -6574,7 +6667,8 @@ def conf_write(all: false) # WRITE TO ~/.rtfm/conf {{{2
|
|
|
6574
6667
|
'history' => "@history = #{@pCmd.history.reverse.uniq.reverse.last(40)}",
|
|
6575
6668
|
'rubyhistory' => "@rubyhistory = #{@pRuby.history.reverse.uniq.reverse.last(40)}",
|
|
6576
6669
|
'aihistory' => "@aihistory = #{@pAI.history.reverse.uniq.reverse.last(40)}",
|
|
6577
|
-
'sshhistory' => "@sshhistory = #{@pSsh.history.reverse.uniq.reverse.last(40)}"
|
|
6670
|
+
'sshhistory' => "@sshhistory = #{@pSsh.history.reverse.uniq.reverse.last(40)}",
|
|
6671
|
+
'directory' => "@directory = #{@directory.select { |d, _| Dir.exist?(d) }.to_a.last(200).to_h}"
|
|
6578
6672
|
}
|
|
6579
6673
|
if all
|
|
6580
6674
|
assignments.merge!(
|
|
@@ -7063,6 +7157,13 @@ end
|
|
|
7063
7157
|
@pSsh.record = true
|
|
7064
7158
|
@pSsh.history = @sshhistory
|
|
7065
7159
|
|
|
7160
|
+
# Restore startup cursor from saved per-directory positions (unless --fresh).
|
|
7161
|
+
# A file-path argument is applied after the first render in the main loop,
|
|
7162
|
+
# since @files isn't populated until render() runs.
|
|
7163
|
+
if !@fresh && @directory[Dir.pwd]
|
|
7164
|
+
@index = @directory[Dir.pwd]
|
|
7165
|
+
end
|
|
7166
|
+
|
|
7066
7167
|
# Report plugin errors {{{2
|
|
7067
7168
|
@pR.say("Plugin load errors:\n" + @plugin_errors.join("\n").fg(196)) if @plugin_errors.any?
|
|
7068
7169
|
|
|
@@ -7096,6 +7197,23 @@ loop do
|
|
|
7096
7197
|
@pB.update = false
|
|
7097
7198
|
end
|
|
7098
7199
|
|
|
7200
|
+
# Check async `:` shell command (non-interactive branch in command_mode)
|
|
7201
|
+
if @shell_cmd_complete
|
|
7202
|
+
@shell_cmd_complete = false
|
|
7203
|
+
@shell_cmd_running = false
|
|
7204
|
+
@shell_cmd_thread = nil
|
|
7205
|
+
if @shell_cmd_output && !@shell_cmd_output.empty?
|
|
7206
|
+
clear_image
|
|
7207
|
+
@pR.say(@shell_cmd_output)
|
|
7208
|
+
end
|
|
7209
|
+
@pB.say(" Done: #{@shell_cmd_label}".fg(156))
|
|
7210
|
+
@shell_cmd_label = nil
|
|
7211
|
+
@shell_cmd_output = nil
|
|
7212
|
+
# Directory may have changed (file created/removed); invalidate cache
|
|
7213
|
+
@dir_cache.delete_if { |key, _| key.start_with?("#{Dir.pwd}:") }
|
|
7214
|
+
@pL.update = @pR.update = @pB.update = true
|
|
7215
|
+
end
|
|
7216
|
+
|
|
7099
7217
|
# Handle pending terminal resize (debounced from SIGWINCH)
|
|
7100
7218
|
if @winch_pending && !@external_program_running
|
|
7101
7219
|
@winch_pending = false
|
|
@@ -7115,6 +7233,15 @@ loop do
|
|
|
7115
7233
|
rescue Errno::EIO
|
|
7116
7234
|
# Note: rcurses 4.9.0+ has enhanced error handling, reducing need for this
|
|
7117
7235
|
end
|
|
7236
|
+
# Apply file-argument cursor selection on first render (when @files is populated)
|
|
7237
|
+
if @startup_select && @files && !@files.empty?
|
|
7238
|
+
ix = @files.index(@startup_select)
|
|
7239
|
+
if ix
|
|
7240
|
+
@index = ix
|
|
7241
|
+
@pL.update = @pR.update = true
|
|
7242
|
+
end
|
|
7243
|
+
@startup_select = nil
|
|
7244
|
+
end
|
|
7118
7245
|
# read key, but ignore TTY-focus errors
|
|
7119
7246
|
begin
|
|
7120
7247
|
getkey
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rtfm-filemanager
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.
|
|
4
|
+
version: 8.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rcurses
|
|
@@ -104,7 +103,6 @@ licenses:
|
|
|
104
103
|
- Unlicense
|
|
105
104
|
metadata:
|
|
106
105
|
source_code_uri: https://github.com/isene/RTFM
|
|
107
|
-
post_install_message:
|
|
108
106
|
rdoc_options: []
|
|
109
107
|
require_paths:
|
|
110
108
|
- lib
|
|
@@ -119,8 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
119
117
|
- !ruby/object:Gem::Version
|
|
120
118
|
version: '0'
|
|
121
119
|
requirements: []
|
|
122
|
-
rubygems_version: 3.
|
|
123
|
-
signing_key:
|
|
120
|
+
rubygems_version: 3.6.7
|
|
124
121
|
specification_version: 4
|
|
125
122
|
summary: RTFM - Ruby Terminal File Manager
|
|
126
123
|
test_files: []
|