rufio 0.65.0 → 0.71.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c80ee03ac65fdfbf06623c7702795bdbaced929d40e72739f599d43eb8d240ef
4
- data.tar.gz: 8bc1a41cd84d762923e6f4785542c8ad4de418c804d0b30150d4217dac0168bd
3
+ metadata.gz: b269821deb0be4866afdc75e58a1369de3448324cee49e6c4f7e68febbae9fff
4
+ data.tar.gz: 835570061c06d6a5adc439d2dfb67c62313fccf101cdb6fb068976f5d72d5607
5
5
  SHA512:
6
- metadata.gz: c42bffef8f63ed300ef17a960625e0e5b5b20735e1f326fba26691e7731c37a0f24a09194b2c51d10e758e4c44710f0e938781e47e0662ba63a1b7afce760195
7
- data.tar.gz: 7033a73f5a0c124eb9fa722e614a7a28f17972872a4cbe8d7682558ba7760852c3c10db6226e5771222ddb94e1f9a68dbe686f1b621c0dee6ae45073aae47a2a
6
+ metadata.gz: 8ba615e62db5874737085807f29825e92c3447a094aeb7349c77b8d0adab4e39fc2ce4bfe40fa53f354b23057e81a29077a7177a6f31379cf140ff876bd2bbc7
7
+ data.tar.gz: b8ac577e995fb0a1cbc7776246f3443e2d137914a84fa94b02b013a3bca5f9124b30b42b03cb6fedbe8bb5d33fcadfbea4d9622023b446df5abb5288871bf978
data/CHANGELOG.md CHANGED
@@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.71.0] - 2026-02-16
11
+
12
+ ### Added
13
+ - **Script Arguments**: Pass arguments to scripts via command mode (e.g., `@retag.sh v0.70.0`)
14
+ - Supports both ScriptRunner and LocalScriptScanner
15
+ - **Script/Rake Execution Logging**: Log execution results of `@script` and `rake:task` commands to CommandLogger
16
+ - Covers both synchronous and background execution
17
+
18
+ ### Fixed
19
+ - **Selection across directories**: Clear selection state when navigating to a different directory
20
+ - **Help/Log viewer entries**: Exclude `..` entry in help mode and log viewer mode
21
+
22
+ ## [0.70.0] - 2026-02-14
23
+
24
+ ### Added
25
+ - **Local Script Scanning**: Automatically detect script files (`.sh`, `.rb`, `.py`, `.pl`, `.js`, `.ts`, `.ps1`) in the currently browsed directory
26
+ - Displayed and executable as `@script.sh` format in command mode
27
+ - Falls back to local scripts when not found in registered script paths
28
+ - Cached per directory with automatic invalidation on directory change
29
+ - **Rakefile Task Execution**: Parse Rakefile to extract task names and execute them
30
+ - `rake:task_name` format in command mode (e.g., `:rake:test`, `:rake:build`)
31
+ - Supports `Rakefile`, `rakefile`, `Rakefile.rb`
32
+ - Recognizes symbol, string, and hash-style task definitions
33
+ - **Rake Task Tab Completion**: Full Tab completion support for rake tasks
34
+ - `rake:` prefix triggers task-specific completion
35
+ - Partial input (`r`, `rak`, `rake`) also suggests matching `rake:xxx` commands
36
+ - Common prefix completion (e.g., `rak` → `rake:`)
37
+ - **New classes**: `LocalScriptScanner`, `RakefileParser`
38
+ - **New tests**: `test_local_script_scanner.rb` (16 tests), `test_rakefile_parser.rb` (19 tests), `test_command_mode_local.rb` (11 tests), `test_command_completion_local.rb` (13 tests), `test_overlay_clearing.rb` (2 tests)
39
+
40
+ ### Fixed
41
+ - **Floating window border remnants**: Fixed old dialog borders remaining when switching between overlay dialogs (e.g., command prompt → completion list)
42
+ - Added `@screen.clear_overlay` in `show_overlay_dialog` before drawing new dialog
43
+ - Added overlay clearing before each frame's `draw_command_mode_to_overlay` in main loop
44
+ - **CommandResult display artifact**: Fixed command window top border remaining visible when showing command execution results
45
+ - Root cause: `execute_command` called legacy `draw_screen` (direct terminal print) which has a coordinate offset from the overlay-based rendering system
46
+ - Removed `draw_screen` call from `execute_command`, letting the main loop handle re-rendering via the buffer system
47
+
48
+ ### Changed
49
+ - **CommandMode**: Added `update_browsing_directory` method, `rake:` routing, local script fallback
50
+ - **CommandCompletion**: Added rake task and local script completion integration
51
+ - **TerminalUI**: `activate_command_mode` now notifies `CommandMode` of current browsing directory
52
+
53
+ ### Technical Details
54
+ - **New files**: `lib/rufio/local_script_scanner.rb`, `lib/rufio/rakefile_parser.rb`, 5 test files
55
+ - **Modified files**: `lib/rufio.rb`, `lib/rufio/command_mode.rb`, `lib/rufio/command_completion.rb`, `lib/rufio/terminal_ui.rb`
56
+ - **Test coverage**: 821 runs, 2816 assertions, 0 failures
57
+
10
58
  ## [0.65.0] - 2026-02-14
11
59
 
12
60
  ### Changed
data/README.md CHANGED
@@ -145,8 +145,18 @@ Press `:` to enter command mode and execute various commands.
145
145
  ```
146
146
  :@build # @ prefix triggers script completion
147
147
  :@deploy.rb # Execute registered script
148
+ :@local.sh # Scripts in current directory are also available
148
149
  ```
149
150
 
151
+ ### Rake Task Execution
152
+
153
+ ```
154
+ :rake:test # Execute rake task
155
+ :rake:build # Tab completion supported
156
+ ```
157
+
158
+ Rakefile in the current directory is automatically parsed. Supports `Rakefile`, `rakefile`, `Rakefile.rb`.
159
+
150
160
  ### Shell Commands
151
161
 
152
162
  ```
@@ -182,8 +192,11 @@ Press `B` → `3` to open the script path management menu:
182
192
  |-----------|----------|
183
193
  | `.rb` | Ruby |
184
194
  | `.py` | Python |
185
- | `.ps1` | PowerShell |
186
195
  | `.sh` | Shell (bash/zsh) |
196
+ | `.js` | JavaScript (Node.js) |
197
+ | `.ts` | TypeScript (ts-node/deno) |
198
+ | `.pl` | Perl |
199
+ | `.ps1` | PowerShell |
187
200
 
188
201
  ## DSL Commands
189
202
 
data/info/help.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # rufio Help
2
2
 
3
- **rufio** is a terminal-based file manager inspired by Yazi.
3
+ **rufio** is a terminal-based file manager and tool runtime environment.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
7
  - [Basic Operations](#basic-operations)
8
- - [Navigation](#navigation)
9
8
  - [File Operations](#file-operations)
10
9
  - [File Selection & Bulk Operations](#file-selection--bulk-operations)
11
10
  - [Filtering & Search](#filtering--search)
12
- - [Bookmarks](#bookmarks)
13
- - [Other Features](#other-features)
11
+ - [Bookmarks & Navigation](#bookmarks--navigation)
12
+ - [Command Mode](#command-mode)
13
+ - [Tool Runtime](#tool-runtime)
14
+ - [Mode Switching](#mode-switching)
14
15
 
15
16
  ---
16
17
 
@@ -20,10 +21,10 @@
20
21
 
21
22
  | Key | Action |
22
23
  |-----|--------|
23
- | `j` or `↓` | Move down one item |
24
- | `k` or `↑` | Move up one item |
25
- | `h` or `←` | Go to parent directory |
26
- | `l`, `Enter`, or `→` | Enter directory / Select file |
24
+ | `j` or `Down` | Move down one item |
25
+ | `k` or `Up` | Move up one item |
26
+ | `h` or `Left` | Go to parent directory |
27
+ | `l`, `Enter`, or `Right` | Enter directory / Open file |
27
28
  | `g` | Jump to top of list |
28
29
  | `G` | Jump to bottom of list |
29
30
 
@@ -61,33 +62,78 @@
61
62
  - Press `Enter` to apply filter and exit filter mode
62
63
  - Press `ESC` to cancel and clear filter
63
64
 
64
- ### Bookmarks
65
+ ### Bookmarks & Navigation
65
66
 
66
67
  | Key | Action |
67
68
  |-----|--------|
68
69
  | `b` | Add current directory to bookmarks |
70
+ | `B` | Bookmark menu (view/add/remove/script paths) |
69
71
  | `0` | Jump to startup directory |
70
72
  | `1-9` | Jump to bookmark 1-9 |
71
- | `p` | Enter project mode (browse bookmarks) |
73
+ | `z` | Navigate using zoxide history |
74
+
75
+ ---
76
+
77
+ ## Command Mode
78
+
79
+ Press `:` to enter command mode. Available prefixes:
80
+
81
+ | Prefix | Function | Example |
82
+ |--------|----------|---------|
83
+ | `@` | Execute script | `:@build.sh` |
84
+ | `rake:` | Execute rake task | `:rake:test` |
85
+ | `!` | Shell command | `:!git status` |
86
+ | (none) | Built-in command | `:hello` |
72
87
 
73
- **Project Mode:**
74
- - Browse all bookmarks with normal navigation keys
75
- - Press `Space` to select a bookmark and jump to it
76
- - Press `ESC` to exit project mode
88
+ ### Features
77
89
 
78
- ### Other Features
90
+ - **Tab completion**: Press `Tab` to auto-complete commands, scripts, and rake tasks
91
+ - **Command history**: Use `Up/Down` arrows to navigate previous commands
92
+ - **Local scripts**: Scripts in the current directory are automatically available with `@` prefix
93
+ - **Rakefile parsing**: Tasks from `Rakefile` in the current directory are available as `rake:task_name`
94
+
95
+ ---
96
+
97
+ ## Tool Runtime
79
98
 
80
99
  | Key | Action |
81
100
  |-----|--------|
82
- | `z` | Navigate using zoxide history |
83
101
  | `:` | Enter command mode |
84
- | `?` | Enter help mode (this mode) |
85
- | `ESC` | Exit help mode / Cancel filter |
86
- | `q` | Quit rufio |
102
+ | `J` | Job mode (view background jobs) |
103
+ | `L` | View execution logs |
104
+
105
+ ### Script Path Management
106
+
107
+ Press `B` then select script path management to:
108
+ - View registered script directories
109
+ - Add/remove script paths
110
+ - Jump to script directories
111
+
112
+ ### Supported Script Types
113
+
114
+ | Extension | Language |
115
+ |-----------|----------|
116
+ | `.rb` | Ruby |
117
+ | `.py` | Python |
118
+ | `.sh` | Shell (bash/zsh) |
119
+ | `.js` | JavaScript |
120
+ | `.ts` | TypeScript |
121
+ | `.pl` | Perl |
122
+ | `.ps1` | PowerShell |
87
123
 
88
124
  ---
89
125
 
90
- ## Help Mode
126
+ ## Mode Switching
127
+
128
+ | Key | Action |
129
+ |-----|--------|
130
+ | `Tab` | Switch mode: Files -> Logs -> Jobs -> Help |
131
+ | `Shift+Tab` | Switch mode (reverse) |
132
+ | `?` | Enter help mode (this mode) |
133
+ | `ESC` | Exit current mode |
134
+ | `q` | Quit rufio |
135
+
136
+ ### Help Mode
91
137
 
92
138
  You are currently in **Help Mode**. In this mode:
93
139
 
@@ -104,7 +150,8 @@ You are currently in **Help Mode**. In this mode:
104
150
  2. **Bulk operations**: Select multiple files with `Space`, then use `m/c/x`
105
151
  3. **Filter + Search**: Use `f` for real-time filtering, `s` for fuzzy search
106
152
  4. **Bookmarks**: Save frequently used directories with `b`, access with `1-9`
107
- 5. **Project mode**: Press `p` to see all bookmarks at once
153
+ 5. **Rake tasks**: If your project has a Rakefile, use `:rake:` with Tab completion
154
+ 6. **Background jobs**: Long-running commands execute in background, view with `J`
108
155
 
109
156
  ---
110
157
 
data/info/keybindings.md CHANGED
@@ -6,10 +6,10 @@ Complete reference of all keyboard shortcuts in **rufio**.
6
6
 
7
7
  | Key | Action |
8
8
  |-----|--------|
9
- | `j` / `↓` | Move down |
10
- | `k` / `↑` | Move up |
11
- | `h` / `←` | Go to parent directory |
12
- | `l` / `→` / `Enter` | Enter directory or select file |
9
+ | `j` / `Down` | Move down |
10
+ | `k` / `Up` | Move up |
11
+ | `h` / `Left` | Go to parent directory |
12
+ | `l` / `Right` / `Enter` | Enter directory or open file |
13
13
  | `g` | Jump to top of list |
14
14
  | `G` | Jump to bottom of list |
15
15
 
@@ -42,31 +42,32 @@ Complete reference of all keyboard shortcuts in **rufio**.
42
42
  | `s` | Search file names with fzf |
43
43
  | `F` | Search file contents with rga |
44
44
 
45
- ## Bookmarks
45
+ ## Bookmarks & Navigation
46
46
 
47
47
  | Key | Action |
48
48
  |-----|--------|
49
49
  | `b` | Add current directory to bookmarks |
50
+ | `B` | Bookmark menu (view/add/remove/script paths) |
50
51
  | `0` | Jump to startup directory |
51
- | `1` | Jump to bookmark 1 |
52
- | `2` | Jump to bookmark 2 |
53
- | `3` | Jump to bookmark 3 |
54
- | `4` | Jump to bookmark 4 |
55
- | `5` | Jump to bookmark 5 |
56
- | `6` | Jump to bookmark 6 |
57
- | `7` | Jump to bookmark 7 |
58
- | `8` | Jump to bookmark 8 |
59
- | `9` | Jump to bookmark 9 |
60
- | `p` | Enter project mode (browse all bookmarks) |
61
-
62
- ## Other
52
+ | `1-9` | Jump to bookmark 1-9 |
53
+ | `z` | Navigate using zoxide history |
54
+
55
+ ## Command Mode & Tool Runtime
63
56
 
64
57
  | Key | Action |
65
58
  |-----|--------|
66
- | `z` | Navigate using zoxide history |
67
59
  | `:` | Enter command mode |
60
+ | `J` | Job mode (view background jobs) |
61
+ | `L` | View execution logs |
62
+
63
+ ## Mode Switching
64
+
65
+ | Key | Action |
66
+ |-----|--------|
67
+ | `Tab` | Switch mode: Files -> Logs -> Jobs -> Help |
68
+ | `Shift+Tab` | Switch mode (reverse direction) |
68
69
  | `?` | Enter help mode |
69
- | `ESC` | Exit current mode (help/filter/project) |
70
+ | `ESC` | Exit current mode (help/filter/command) |
70
71
  | `q` | Quit rufio |
71
72
 
72
73
  ---
@@ -79,15 +80,28 @@ Complete reference of all keyboard shortcuts in **rufio**.
79
80
  - **Enter**: Apply filter and exit filter mode
80
81
  - **ESC**: Cancel filter and exit filter mode
81
82
 
82
- ### Project Mode
83
- - **j/k**: Navigate through bookmarks
84
- - **Space**: Select bookmark and jump to directory
85
- - **ESC**: Exit project mode
83
+ ### Command Mode
84
+ - **Any character**: Add to command input
85
+ - **Tab**: Auto-complete (scripts, rake tasks, commands)
86
+ - **Up/Down**: Navigate command history
87
+ - **Enter**: Execute command
88
+ - **Backspace**: Remove last character
89
+ - **ESC**: Cancel and exit command mode
90
+
91
+ ### Command Mode Prefixes
92
+ - `@script` - Execute a registered or local script
93
+ - `rake:task` - Execute a Rakefile task
94
+ - `!command` - Execute a shell command
95
+ - `command` - Execute a built-in or plugin command
86
96
 
87
97
  ### Help Mode
88
98
  - **j/k/h/l**: Navigate help files
89
99
  - **ESC**: Exit help mode and return to previous directory
90
100
 
101
+ ### Job Mode
102
+ - **j/k**: Navigate through jobs
103
+ - **ESC**: Exit job mode
104
+
91
105
  ---
92
106
 
93
107
  ## Tips
@@ -96,6 +110,8 @@ Complete reference of all keyboard shortcuts in **rufio**.
96
110
  - **Selection**: Use `Space` to mark multiple files, then operate on all at once
97
111
  - **Quick access**: Number keys `1-9` provide instant access to bookmarks
98
112
  - **Filtering**: Press `f` and start typing for real-time filtering
113
+ - **Rake tasks**: Type `:rake:` then `Tab` to see available tasks
114
+ - **Local scripts**: Scripts in the current directory are auto-detected with `@` prefix
99
115
 
100
116
  ---
101
117
 
data/info/welcome.md CHANGED
@@ -1,15 +1,18 @@
1
- # Welcome to rufio! 🚀
1
+ # Welcome to rufio!
2
2
 
3
- Thank you for using **rufio** - a terminal-based file manager inspired by [Yazi](https://github.com/sxyazi/yazi).
3
+ **Runtime Unified Flow I/O Operator**
4
+
5
+ A TUI file manager that executes and coordinates tools and scripts from files.
4
6
 
5
7
  ## Key Features
6
8
 
7
- - **Vim-like keybindings** - Navigate with `j/k/h/l` keys
8
- - **Real-time filtering** - Press `f` to filter files instantly
9
- - **Advanced search** - Use `fzf` for file names and `rga` for content search
10
- - **Bookmark support** - Quick access to favorite directories with number keys
11
- - **Project mode** - Manage multiple projects efficiently
12
- - **Plugin system** - Extend functionality with custom plugins
9
+ - **Tool Runtime** - Execute scripts and rake tasks from command mode
10
+ - **Vim-like Keybindings** - Navigate with `j/k/h/l` keys
11
+ - **Real-time Filtering** - Press `f` to filter files instantly
12
+ - **Advanced Search** - Use `fzf` for file names and `rga` for content search
13
+ - **Bookmark Support** - Quick access to favorite directories
14
+ - **Job Management** - Run commands in the background
15
+ - **Plugin System** - Extend functionality with custom plugins
13
16
 
14
17
  ## Quick Start
15
18
 
@@ -18,12 +21,25 @@ Thank you for using **rufio** - a terminal-based file manager inspired by [Yazi]
18
21
  | `j/k` | Move down/up |
19
22
  | `h/l` | Go to parent / Enter directory |
20
23
  | `f` | Filter files |
24
+ | `s` | Search with fzf |
21
25
  | `b` | Add bookmark |
22
- | `p` | Project mode |
23
- | `?` | Help mode |
26
+ | `B` | Bookmark menu |
27
+ | `z` | zoxide history |
24
28
  | `:` | Command mode |
29
+ | `J` | Job mode |
30
+ | `?` | Help mode |
31
+ | `Tab` | Switch mode (Files / Logs / Jobs / Help) |
25
32
  | `q` | Quit |
26
33
 
34
+ ## Command Mode
35
+
36
+ Press `:` to enter command mode:
37
+
38
+ - `@script.sh` - Execute a script
39
+ - `rake:test` - Execute a rake task
40
+ - `!git status` - Run a shell command
41
+ - `Tab` - Auto-complete commands
42
+
27
43
  ## Getting Help
28
44
 
29
45
  Press `?` to enter **Help Mode** where you can browse all documentation files.
@@ -37,4 +53,4 @@ Visit the GitHub repository for detailed documentation:
37
53
 
38
54
  ---
39
55
 
40
- *Happy file managing! 📁*
56
+ *Happy file managing!*
@@ -16,9 +16,11 @@ module Rufio
16
16
  # @param input [String] 入力されたテキスト
17
17
  # @return [Array<String>] 補完候補のリスト
18
18
  def complete(input)
19
- # 入力が空の場合は内部コマンドとスクリプトを返す
19
+ # 入力が空の場合は内部コマンド + スクリプト + rakeタスクを返す
20
20
  if input.nil? || input.strip.empty?
21
- return @command_mode.available_commands.map(&:to_s) + script_candidates('')
21
+ return @command_mode.available_commands.map(&:to_s) +
22
+ script_candidates('') +
23
+ rake_candidates('')
22
24
  end
23
25
 
24
26
  # シェルコマンド補完(!で始まる場合)
@@ -31,13 +33,23 @@ module Rufio
31
33
  return @command_mode.complete_script(input.strip)
32
34
  end
33
35
 
34
- # 通常のコマンド補完(内部コマンド + スクリプト)
36
+ # rakeタスク補完(rake:で始まる場合)
37
+ if input.strip.start_with?('rake:')
38
+ prefix = input.strip[5..-1]
39
+ return @command_mode.complete_rake_task(prefix)
40
+ end
41
+
42
+ # 通常のコマンド補完(内部コマンド + rakeタスク)
35
43
  available_commands = @command_mode.available_commands.map(&:to_s)
36
44
  input_lower = input.downcase
37
45
  candidates = available_commands.select do |command|
38
46
  command.downcase.start_with?(input_lower)
39
47
  end
40
48
 
49
+ # rakeタスクも候補に追加("r", "ra", "rak", "rake" 等にマッチ)
50
+ rake_tasks = rake_candidates('')
51
+ candidates += rake_tasks.select { |task| task.downcase.start_with?(input_lower) }
52
+
41
53
  candidates
42
54
  end
43
55
 
@@ -67,15 +79,20 @@ module Rufio
67
79
 
68
80
  private
69
81
 
70
- # スクリプト候補を取得
82
+ # スクリプト候補を取得(ScriptRunner + ローカルスクリプト)
71
83
  # @param prefix [String] 入力中の文字列
72
84
  # @return [Array<String>] スクリプト候補(@付き)
73
85
  def script_candidates(prefix)
74
- return [] unless @command_mode&.script_runner
75
-
76
86
  @command_mode.complete_script("@#{prefix}")
77
87
  end
78
88
 
89
+ # rakeタスク候補を取得
90
+ # @param prefix [String] 入力中の文字列
91
+ # @return [Array<String>] rakeタスク候補(rake:付き)
92
+ def rake_candidates(prefix)
93
+ @command_mode.complete_rake_task(prefix)
94
+ end
95
+
79
96
  # シェルコマンドの補完
80
97
  # @param input [String] ! で始まる入力
81
98
  # @return [Array<String>] 補完候補のリスト
@@ -15,6 +15,8 @@ module Rufio
15
15
  @script_runner = nil
16
16
  @script_path_manager = nil
17
17
  @job_manager = nil
18
+ @local_script_scanner = LocalScriptScanner.new
19
+ @rakefile_parser = RakefileParser.new
18
20
  load_builtin_commands
19
21
  load_dsl_commands
20
22
  end
@@ -26,7 +28,8 @@ module Rufio
26
28
  @job_manager = job_manager
27
29
  @script_runner = ScriptRunner.new(
28
30
  script_paths: script_paths,
29
- job_manager: job_manager
31
+ job_manager: job_manager,
32
+ command_logger: @background_executor&.command_logger
30
33
  )
31
34
  end
32
35
 
@@ -39,10 +42,18 @@ module Rufio
39
42
  # ScriptRunnerも設定(ScriptPathManagerのパスを使用)
40
43
  @script_runner = ScriptRunner.new(
41
44
  script_paths: @script_path_manager.paths,
42
- job_manager: job_manager
45
+ job_manager: job_manager,
46
+ command_logger: @background_executor&.command_logger
43
47
  )
44
48
  end
45
49
 
50
+ # 閲覧中ディレクトリを更新
51
+ # @param directory [String] 現在の閲覧ディレクトリ
52
+ def update_browsing_directory(directory)
53
+ @local_script_scanner.update_directory(directory)
54
+ @rakefile_parser.update_directory(directory)
55
+ end
56
+
46
57
  # コマンドを実行する
47
58
  # @param command_string [String] コマンド文字列
48
59
  # @param working_dir [String, nil] 作業ディレクトリ(スクリプト実行時に使用)
@@ -55,6 +66,12 @@ module Rufio
55
66
  return execute_script(command_string.strip[1..-1], working_dir)
56
67
  end
57
68
 
69
+ # rakeタスク実行 (rake: で始まる場合)
70
+ if command_string.strip.start_with?('rake:')
71
+ task_name = command_string.strip[5..-1]
72
+ return execute_rake_task(task_name, working_dir)
73
+ end
74
+
58
75
  # シェルコマンドの実行 (! で始まる場合)
59
76
  if command_string.strip.start_with?('!')
60
77
  shell_command = command_string.strip[1..-1]
@@ -132,11 +149,28 @@ module Rufio
132
149
  # @param prefix [String] 入力中の文字列(@を含む)
133
150
  # @return [Array<String>] 補完候補(@付き)
134
151
  def complete_script(prefix)
135
- return [] unless @script_runner
136
-
137
152
  # @を除去して検索
138
153
  search_prefix = prefix.sub(/^@/, '')
139
- @script_runner.complete(search_prefix).map { |name| "@#{name}" }
154
+
155
+ candidates = []
156
+
157
+ # ScriptRunnerからの候補
158
+ if @script_runner
159
+ candidates += @script_runner.complete(search_prefix)
160
+ end
161
+
162
+ # ローカルスクリプトからの候補
163
+ candidates += @local_script_scanner.complete(search_prefix)
164
+
165
+ # 重複排除してソート、@付きで返す
166
+ candidates.uniq.sort.map { |name| "@#{name}" }
167
+ end
168
+
169
+ # rakeタスク名を補完する
170
+ # @param prefix [String] 入力中の文字列(rake:を含まない)
171
+ # @return [Array<String>] 補完候補(rake:付き)
172
+ def complete_rake_task(prefix)
173
+ @rakefile_parser.complete(prefix).map { |name| "rake:#{name}" }
140
174
  end
141
175
 
142
176
  private
@@ -204,22 +238,186 @@ module Rufio
204
238
  end
205
239
 
206
240
  # スクリプトを実行する(@プレフィックス用)
207
- # @param script_name [String] スクリプト名
241
+ # ScriptRunner LocalScriptScanner の順にフォールバック
242
+ # @param script_input [String] スクリプト名(引数を含む場合あり)
208
243
  # @param working_dir [String, nil] 作業ディレクトリ
209
244
  # @return [String] 実行結果メッセージ
210
- def execute_script(script_name, working_dir)
211
- unless @script_runner
212
- return "⚠️ スクリプトランナーが設定されていません"
245
+ def execute_script(script_input, working_dir)
246
+ working_dir ||= Dir.pwd
247
+
248
+ # スクリプト名と引数を分離(例: "retag.sh v0.70.0" → name="retag.sh", args="v0.70.0")
249
+ parts = script_input.split(' ', 2)
250
+ script_name = parts[0]
251
+ script_args = parts[1]
252
+
253
+ # ScriptRunnerで検索
254
+ if @script_runner
255
+ job = @script_runner.run(script_name, working_dir: working_dir, args: script_args)
256
+ return "🚀 ジョブを開始: #{script_name}" if job
213
257
  end
214
258
 
215
- working_dir ||= Dir.pwd
259
+ # LocalScriptScannerにフォールバック
260
+ local_script = @local_script_scanner.find_script(script_name)
261
+ if local_script
262
+ return execute_local_script(local_script, working_dir, script_args)
263
+ end
216
264
 
217
- job = @script_runner.run(script_name, working_dir: working_dir)
265
+ # どちらにも見つからない
266
+ if @script_runner
267
+ "⚠️ スクリプトが見つかりません: #{script_name}"
268
+ else
269
+ "⚠️ スクリプトランナーが設定されていません"
270
+ end
271
+ end
218
272
 
219
- if job
220
- "🚀 ジョブを開始: #{script_name}"
273
+ # ローカルスクリプトを実行する
274
+ # @param script [Hash] スクリプト情報 { name:, path:, dir: }
275
+ # @param working_dir [String] 作業ディレクトリ
276
+ # @param args [String, nil] スクリプトに渡す引数
277
+ # @return [String, Hash] 実行結果メッセージ
278
+ def execute_local_script(script, working_dir, args = nil)
279
+ command = build_script_command(script)
280
+ command = "#{command} #{args}" if args && !args.empty?
281
+
282
+ if @job_manager
283
+ job = @job_manager.add_job(
284
+ name: script[:name],
285
+ path: working_dir,
286
+ command: command
287
+ )
288
+ job.start
289
+
290
+ Thread.new do
291
+ execute_script_in_background(job, script, working_dir, command)
292
+ end
293
+
294
+ "🚀 ジョブを開始: #{script[:name]}"
221
295
  else
222
- "⚠️ スクリプトが見つかりません: #{script_name}"
296
+ # 同期実行
297
+ stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
298
+ result = {
299
+ success: status.success?,
300
+ output: stdout.strip,
301
+ stderr: stderr.strip
302
+ }
303
+
304
+ # Logsに記録
305
+ log_execution("@#{script[:name]}", result)
306
+
307
+ result
308
+ end
309
+ end
310
+
311
+ # 実行結果をCommandLoggerに記録
312
+ # @param command_name [String] コマンド名
313
+ # @param result [Hash] 実行結果 { success:, output:, stderr:, error: }
314
+ def log_execution(command_name, result)
315
+ logger = @background_executor&.command_logger
316
+ return unless logger
317
+
318
+ output = [result[:output], result[:stderr]].compact.reject(&:empty?).join("\n")
319
+ logger.log(
320
+ command_name,
321
+ output,
322
+ success: result[:success],
323
+ error: result[:error]
324
+ )
325
+ end
326
+
327
+ # スクリプトの実行コマンドを構築
328
+ # @param script [Hash] スクリプト情報
329
+ # @return [String] 実行コマンド
330
+ def build_script_command(script)
331
+ path = script[:path]
332
+ ext = File.extname(path).downcase
333
+
334
+ case ext
335
+ when '.rb'
336
+ "ruby #{path.shellescape}"
337
+ when '.py'
338
+ "python3 #{path.shellescape}"
339
+ when '.js'
340
+ "node #{path.shellescape}"
341
+ when '.ts'
342
+ "ts-node #{path.shellescape}"
343
+ when '.pl'
344
+ "perl #{path.shellescape}"
345
+ when '.ps1'
346
+ "pwsh #{path.shellescape}"
347
+ else
348
+ path.shellescape
349
+ end
350
+ end
351
+
352
+ # ローカルスクリプトをバックグラウンドで実行
353
+ def execute_script_in_background(job, script, working_dir, command)
354
+ stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
355
+
356
+ job.append_log(stdout) unless stdout.empty?
357
+ job.append_log(stderr) unless stderr.empty?
358
+
359
+ if status.success?
360
+ job.complete(exit_code: status.exitstatus)
361
+ else
362
+ job.fail(exit_code: status.exitstatus)
363
+ end
364
+
365
+ # Logsに記録
366
+ log_execution("@#{script[:name]}", {
367
+ success: status.success?,
368
+ output: stdout.strip,
369
+ stderr: stderr.strip
370
+ })
371
+
372
+ @job_manager&.notify_completion(job)
373
+ rescue StandardError => e
374
+ job.append_log("Error: #{e.message}")
375
+ job.fail(exit_code: -1)
376
+ log_execution("@#{script[:name]}", { success: false, output: '', stderr: e.message })
377
+ @job_manager&.notify_completion(job)
378
+ end
379
+
380
+ # rakeタスクを実行する
381
+ # @param task_name [String] タスク名
382
+ # @param working_dir [String, nil] 作業ディレクトリ
383
+ # @return [String, Hash] 実行結果
384
+ def execute_rake_task(task_name, working_dir)
385
+ unless @rakefile_parser.rakefile_exists?
386
+ return "⚠️ Rakefileが見つかりません"
387
+ end
388
+
389
+ unless @rakefile_parser.tasks.include?(task_name)
390
+ return "⚠️ rakeタスクが見つかりません: #{task_name}"
391
+ end
392
+
393
+ working_dir ||= Dir.pwd
394
+ shell_command = "rake #{task_name.shellescape}"
395
+
396
+ begin
397
+ stdout, stderr, status = Open3.capture3(shell_command, chdir: working_dir)
398
+
399
+ result = {
400
+ success: status.success?,
401
+ output: stdout.strip,
402
+ stderr: stderr.strip
403
+ }
404
+
405
+ unless status.success?
406
+ result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})"
407
+ end
408
+
409
+ # Logsに記録
410
+ log_execution("rake:#{task_name}", result)
411
+
412
+ result
413
+ rescue Errno::ENOENT => e
414
+ result = { success: false, error: "rakeが見つかりません: #{e.message}" }
415
+ log_execution("rake:#{task_name}", result)
416
+ result
417
+ rescue StandardError => e
418
+ result = { success: false, error: "rake実行エラー: #{e.message}" }
419
+ log_execution("rake:#{task_name}", result)
420
+ result
223
421
  end
224
422
  end
225
423
 
@@ -218,10 +218,15 @@ module Rufio
218
218
  end
219
219
 
220
220
  def get_active_entries
221
- if @filter_manager.filter_active?
222
- @filter_manager.filtered_entries
221
+ entries = if @filter_manager.filter_active?
222
+ @filter_manager.filtered_entries
223
+ else
224
+ @directory_listing&.list_entries || []
225
+ end
226
+ if @in_help_mode || @in_log_viewer_mode
227
+ entries.reject { |e| e[:name] == '..' }
223
228
  else
224
- @directory_listing&.list_entries || []
229
+ entries
225
230
  end
226
231
  end
227
232
 
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # 閲覧中ディレクトリのスクリプトファイルを検出するスキャナー
5
+ # カレントディレクトリ直下のスクリプトファイル(.sh, .rb, .py等)を検出し、
6
+ # コマンドモードから @script.sh 形式で実行可能にする
7
+ class LocalScriptScanner
8
+ # サポートするスクリプト拡張子
9
+ SUPPORTED_EXTENSIONS = %w[.sh .rb .py .pl .js .ts .ps1].freeze
10
+
11
+ # @param directory [String, nil] スキャン対象ディレクトリ
12
+ def initialize(directory = nil)
13
+ @directory = directory
14
+ @scripts_cache = nil
15
+ end
16
+
17
+ # ディレクトリ変更時にキャッシュを無効化
18
+ # @param directory [String] 新しいディレクトリ
19
+ def update_directory(directory)
20
+ return if @directory == directory
21
+
22
+ @directory = directory
23
+ @scripts_cache = nil
24
+ end
25
+
26
+ # 利用可能なスクリプト一覧を取得
27
+ # @return [Array<Hash>] スクリプト情報の配列 [{ name:, path:, dir: }, ...]
28
+ def available_scripts
29
+ @scripts_cache ||= scan_scripts
30
+ end
31
+
32
+ # 名前でスクリプトを検索
33
+ # @param name [String] スクリプト名(拡張子あり/なし)
34
+ # @return [Hash, nil] スクリプト情報 { name:, path:, dir: } または nil
35
+ def find_script(name)
36
+ # 完全一致を優先
37
+ script = available_scripts.find { |s| s[:name] == name }
38
+ return script if script
39
+
40
+ # 拡張子なしで検索
41
+ SUPPORTED_EXTENSIONS.each do |ext|
42
+ script = available_scripts.find { |s| s[:name] == "#{name}#{ext}" }
43
+ return script if script
44
+ end
45
+
46
+ nil
47
+ end
48
+
49
+ # Tab補完候補を取得
50
+ # @param prefix [String] 入力中の文字列
51
+ # @return [Array<String>] 補完候補のスクリプト名
52
+ def complete(prefix)
53
+ available_scripts
54
+ .map { |s| s[:name] }
55
+ .select { |name| name.start_with?(prefix) }
56
+ .sort
57
+ end
58
+
59
+ private
60
+
61
+ # ディレクトリをスキャンしてスクリプトファイルを収集
62
+ # @return [Array<Hash>] スクリプト情報の配列
63
+ def scan_scripts
64
+ return [] unless @directory && Dir.exist?(@directory)
65
+
66
+ scripts = []
67
+
68
+ Dir.foreach(@directory) do |entry|
69
+ next if entry.start_with?('.')
70
+ path = File.join(@directory, entry)
71
+ next unless File.file?(path)
72
+ next unless script_file?(entry)
73
+
74
+ scripts << {
75
+ name: entry,
76
+ path: path,
77
+ dir: @directory
78
+ }
79
+ end
80
+
81
+ scripts.sort_by { |s| s[:name] }
82
+ end
83
+
84
+ # スクリプトファイルかどうかを判定
85
+ # @param filename [String] ファイル名
86
+ # @return [Boolean]
87
+ def script_file?(filename)
88
+ ext = File.extname(filename).downcase
89
+ SUPPORTED_EXTENSIONS.include?(ext)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # Rakefileをパースしてタスク名を抽出するパーサー
5
+ # 正規表現ベースでtask定義を検出し、コマンドモードから
6
+ # rake:task_name 形式で実行可能にする
7
+ class RakefileParser
8
+ RAKEFILE_NAMES = %w[Rakefile rakefile Rakefile.rb].freeze
9
+ TASK_PATTERN = /^\s*task\s+[:'"]?(\w+)/.freeze
10
+
11
+ # @param directory [String, nil] スキャン対象ディレクトリ
12
+ def initialize(directory = nil)
13
+ @directory = directory
14
+ @tasks_cache = nil
15
+ end
16
+
17
+ # ディレクトリ変更時にキャッシュを無効化
18
+ # @param directory [String] 新しいディレクトリ
19
+ def update_directory(directory)
20
+ return if @directory == directory
21
+
22
+ @directory = directory
23
+ @tasks_cache = nil
24
+ end
25
+
26
+ # Rakefileが存在するかどうか
27
+ # @return [Boolean]
28
+ def rakefile_exists?
29
+ !!find_rakefile
30
+ end
31
+
32
+ # Rakefileからタスク名を抽出
33
+ # @return [Array<String>] タスク名の配列(ソート済み・ユニーク)
34
+ def tasks
35
+ @tasks_cache ||= parse_tasks
36
+ end
37
+
38
+ # Tab補完候補を取得
39
+ # @param prefix [String] 入力中の文字列
40
+ # @return [Array<String>] 補完候補のタスク名
41
+ def complete(prefix)
42
+ tasks.select { |name| name.start_with?(prefix) }
43
+ end
44
+
45
+ private
46
+
47
+ # Rakefileのパスを検索
48
+ # @return [String, nil] Rakefileのパス、見つからない場合nil
49
+ def find_rakefile
50
+ return nil unless @directory && Dir.exist?(@directory)
51
+
52
+ RAKEFILE_NAMES.each do |name|
53
+ path = File.join(@directory, name)
54
+ return path if File.file?(path)
55
+ end
56
+
57
+ nil
58
+ end
59
+
60
+ # Rakefileをパースしてタスク名を抽出
61
+ # @return [Array<String>] タスク名の配列
62
+ def parse_tasks
63
+ rakefile = find_rakefile
64
+ return [] unless rakefile
65
+
66
+ content = File.read(rakefile)
67
+ task_names = []
68
+
69
+ content.each_line do |line|
70
+ match = line.match(TASK_PATTERN)
71
+ task_names << match[1] if match
72
+ end
73
+
74
+ task_names.uniq.sort
75
+ end
76
+ end
77
+ end
@@ -11,9 +11,11 @@ module Rufio
11
11
 
12
12
  # @param script_paths [Array<String>] スクリプトを検索するディレクトリのリスト
13
13
  # @param job_manager [JobManager, nil] ジョブマネージャー(nilの場合は同期実行)
14
- def initialize(script_paths:, job_manager: nil)
14
+ # @param command_logger [CommandLogger, nil] コマンドロガー
15
+ def initialize(script_paths:, job_manager: nil, command_logger: nil)
15
16
  @script_paths = script_paths.map { |p| File.expand_path(p) }
16
17
  @job_manager = job_manager
18
+ @command_logger = command_logger
17
19
  @scripts_cache = nil
18
20
  end
19
21
 
@@ -54,15 +56,16 @@ module Rufio
54
56
  # スクリプトをジョブとして実行
55
57
  # @param name [String] スクリプト名
56
58
  # @param working_dir [String] 作業ディレクトリ
59
+ # @param args [String, nil] スクリプトに渡す引数
57
60
  # @param selected_file [String, nil] 選択中のファイル
58
61
  # @param selected_dir [String, nil] 選択中のディレクトリ
59
62
  # @return [TaskStatus, nil] 作成されたジョブ、またはスクリプトが見つからない場合nil
60
- def run(name, working_dir:, selected_file: nil, selected_dir: nil)
63
+ def run(name, working_dir:, args: nil, selected_file: nil, selected_dir: nil)
61
64
  script = find_script(name)
62
65
  return nil unless script
63
66
 
64
67
  env = build_environment(working_dir, selected_file, selected_dir)
65
- execute_script(script, working_dir, env)
68
+ execute_script(script, working_dir, env, args)
66
69
  end
67
70
 
68
71
  # キャッシュをクリア
@@ -130,9 +133,11 @@ module Rufio
130
133
  # @param script [Hash] スクリプト情報
131
134
  # @param working_dir [String] 作業ディレクトリ
132
135
  # @param env [Hash] 環境変数
136
+ # @param args [String, nil] スクリプトに渡す引数
133
137
  # @return [TaskStatus] 作成されたジョブ
134
- def execute_script(script, working_dir, env = {})
138
+ def execute_script(script, working_dir, env = {}, args = nil)
135
139
  command = build_command(script)
140
+ command = "#{command} #{args}" if args && !args.empty?
136
141
 
137
142
  if @job_manager
138
143
  # ジョブマネージャーにジョブを追加
@@ -205,12 +210,24 @@ module Rufio
205
210
  job.fail(exit_code: status.exitstatus)
206
211
  end
207
212
 
213
+ # CommandLoggerに記録
214
+ log_to_command_logger(job.name, stdout, stderr, status.success?)
215
+
208
216
  # 通知を送信
209
217
  @job_manager&.notify_completion(job)
210
218
  rescue StandardError => e
211
219
  job.append_log("Error: #{e.message}")
212
220
  job.fail(exit_code: -1)
221
+ log_to_command_logger(job.name, '', e.message, false)
213
222
  @job_manager&.notify_completion(job)
214
223
  end
224
+
225
+ # CommandLoggerに実行結果を記録
226
+ def log_to_command_logger(name, stdout, stderr, success)
227
+ return unless @command_logger
228
+
229
+ output = [stdout, stderr].compact.map(&:strip).reject(&:empty?).join("\n")
230
+ @command_logger.log("@#{name}", output, success: success)
231
+ end
215
232
  end
216
233
  end
@@ -17,6 +17,12 @@ module Rufio
17
17
  def toggle_selection(entry, current_directory = nil)
18
18
  return false unless entry
19
19
 
20
+ # 異なるディレクトリで選択した場合、古い選択をクリアしてリセット
21
+ if current_directory && @source_directory && current_directory != @source_directory
22
+ @selected_items.clear
23
+ @source_directory = nil
24
+ end
25
+
20
26
  if @selected_items.include?(entry[:name])
21
27
  @selected_items.delete(entry[:name])
22
28
  # Clear source_directory if no items are selected
@@ -216,6 +216,7 @@ module Rufio
216
216
  last_frame_time = Time.now
217
217
  current_fps = 0.0
218
218
  last_fps_update = Time.now
219
+ @last_displayed_fps = 0.0
219
220
 
220
221
  # 再描画フラグ
221
222
  needs_redraw = false
@@ -237,8 +238,11 @@ module Rufio
237
238
  last_fps_update = start
238
239
  end
239
240
 
240
- # test_modeでは毎フレーム描画してFPS計測の精度を上げる
241
- needs_redraw = true
241
+ # FPS表示の更新タイミングで再描画(1秒ごと)
242
+ if current_fps != @last_displayed_fps
243
+ @last_displayed_fps = current_fps
244
+ needs_redraw = true
245
+ end
242
246
  end
243
247
 
244
248
  # UPDATE phase - ノンブロッキング入力処理
@@ -305,6 +309,8 @@ module Rufio
305
309
 
306
310
  # コマンドモードがアクティブな場合はオーバーレイにダイアログを描画
307
311
  if @command_mode_active
312
+ # 前回のオーバーレイ残留を防ぐためクリアしてから描画
313
+ @screen.clear_overlay if @screen.overlay_enabled?
308
314
  draw_command_mode_to_overlay
309
315
  else
310
316
  # コマンドモードでない場合はオーバーレイをクリア
@@ -1370,6 +1376,9 @@ module Rufio
1370
1376
  def activate_command_mode
1371
1377
  @command_mode_active = true
1372
1378
  @command_input = ""
1379
+ # 閲覧中ディレクトリをコマンドモードに通知(ローカルスクリプト・Rakefileの検出用)
1380
+ browsing_dir = @directory_listing&.current_path || Dir.pwd
1381
+ @command_mode.update_browsing_directory(browsing_dir)
1373
1382
  end
1374
1383
 
1375
1384
  # コマンドモードを終了
@@ -1476,8 +1485,9 @@ module Rufio
1476
1485
  @command_mode_ui.show_result(result)
1477
1486
  end
1478
1487
 
1479
- # 画面を再描画
1480
- draw_screen
1488
+ # メインループの次フレームで再描画される(draw_screenは使わない)
1489
+ # draw_screen(レガシー直接出力)はバッファベースのオーバーレイと座標系が異なるため、
1490
+ # 使用するとコマンドプロンプトの枠線が残る不具合が発生する
1481
1491
  end
1482
1492
 
1483
1493
  # Tab補完を処理
@@ -1697,8 +1707,9 @@ module Rufio
1697
1707
  def show_overlay_dialog(title, content_lines, options = {}, &block)
1698
1708
  return nil unless @screen && @renderer
1699
1709
 
1700
- # オーバーレイを有効化
1710
+ # オーバーレイを有効化し、前回のダイアログ残留を除去
1701
1711
  @screen.enable_overlay
1712
+ @screen.clear_overlay
1702
1713
 
1703
1714
  # ウィンドウサイズを計算
1704
1715
  if options[:width] && options[:height]
data/lib/rufio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rufio
4
- VERSION = '0.65.0'
4
+ VERSION = '0.71.0'
5
5
  end
data/lib/rufio.rb CHANGED
@@ -53,6 +53,8 @@ require_relative 'rufio/job_mode'
53
53
  require_relative 'rufio/script_runner'
54
54
  require_relative 'rufio/script_path_manager'
55
55
  require_relative 'rufio/script_config_loader'
56
+ require_relative 'rufio/local_script_scanner'
57
+ require_relative 'rufio/rakefile_parser'
56
58
 
57
59
  module Rufio
58
60
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.65.0
4
+ version: 0.71.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - masisz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-14 00:00:00.000000000 Z
11
+ date: 2026-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -175,12 +175,14 @@ files:
175
175
  - lib/rufio/job_manager.rb
176
176
  - lib/rufio/job_mode.rb
177
177
  - lib/rufio/keybind_handler.rb
178
+ - lib/rufio/local_script_scanner.rb
178
179
  - lib/rufio/logger.rb
179
180
  - lib/rufio/native/rufio_zig.bundle
180
181
  - lib/rufio/native_scanner.rb
181
182
  - lib/rufio/native_scanner_zig.rb
182
183
  - lib/rufio/notification_manager.rb
183
184
  - lib/rufio/parallel_scanner.rb
185
+ - lib/rufio/rakefile_parser.rb
184
186
  - lib/rufio/renderer.rb
185
187
  - lib/rufio/screen.rb
186
188
  - lib/rufio/script_config_loader.rb