hiiro 0.1.54 → 0.1.56
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/bin/h-app +12 -18
- data/bin/h-plugin +2 -1
- data/exe/h +50 -47
- data/lib/hiiro/history/entry.rb +3 -2
- data/lib/hiiro/history.rb +2 -2
- data/lib/hiiro/prefix_matcher.rb +110 -0
- data/lib/hiiro/version.rb +1 -1
- data/lib/hiiro.rb +1 -0
- data/plugins/pins.rb +2 -6
- data/plugins/tasks.rb +128 -106
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0db0a8fd9d54e542cb2b5951197e2250a1d199a66278b31bab5c003b50d8f39f
|
|
4
|
+
data.tar.gz: bee3a32c0dc9e68379aee3963bb11b15041a9cd21715379dbbc086bff59eba17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b59060b23668b9f6fc2818884628dccae6a95e84a5554d9666b9ce79d9bfc45f231c486039121c6a33cc8e97b144e1579cbd637131d8ca5cd0f196bfcdb4e9ff
|
|
7
|
+
data.tar.gz: 5918af1c725ce23f22a17d273def7f656118c1a54f9fd13ee7fe0be708e82f8c8146c7a4b4c7860482c5fb85a5c8a86910c5412062a96d9c37ce9901b527bee7
|
data/bin/h-app
CHANGED
|
@@ -62,23 +62,21 @@ Hiiro.run(*ARGV, plugins: [:Tasks]) {
|
|
|
62
62
|
next
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
match = apps.find { |name, _| name.start_with?(app_name) }
|
|
65
|
+
app = environment.app_matcher.find(app_name)
|
|
67
66
|
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
target = File.join(root, app_relative_path)
|
|
67
|
+
if app
|
|
68
|
+
target = File.join(root, app.relative_path)
|
|
71
69
|
send_cd(relative_cd_path(target))
|
|
72
70
|
else
|
|
73
71
|
puts "App '#{app_name}' not found"
|
|
74
72
|
puts
|
|
75
73
|
puts "Available apps:"
|
|
76
|
-
|
|
74
|
+
environment.all_apps.each { |a| puts format(" %-20s => %s", a.name, a.relative_path) }
|
|
77
75
|
end
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
add_subcmd(:ls) {
|
|
81
|
-
apps =
|
|
79
|
+
apps = environment.all_apps
|
|
82
80
|
|
|
83
81
|
if apps.empty?
|
|
84
82
|
puts "No apps configured."
|
|
@@ -87,7 +85,7 @@ Hiiro.run(*ARGV, plugins: [:Tasks]) {
|
|
|
87
85
|
else
|
|
88
86
|
puts "Configured apps:"
|
|
89
87
|
puts
|
|
90
|
-
apps.each { |
|
|
88
|
+
apps.each { |a| puts format(" %-20s => %s", a.name, a.relative_path) }
|
|
91
89
|
end
|
|
92
90
|
}
|
|
93
91
|
|
|
@@ -104,12 +102,10 @@ Hiiro.run(*ARGV, plugins: [:Tasks]) {
|
|
|
104
102
|
next
|
|
105
103
|
end
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
match = apps.find { |name, _| name.start_with?(app_name) }
|
|
105
|
+
app = environment.app_matcher.find(app_name)
|
|
109
106
|
|
|
110
|
-
if
|
|
111
|
-
|
|
112
|
-
target = File.join(root, app_relative_path)
|
|
107
|
+
if app
|
|
108
|
+
target = File.join(root, app.relative_path)
|
|
113
109
|
puts relative_cd_path(target)
|
|
114
110
|
else
|
|
115
111
|
puts "App '#{app_name}' not found"
|
|
@@ -129,12 +125,10 @@ Hiiro.run(*ARGV, plugins: [:Tasks]) {
|
|
|
129
125
|
next
|
|
130
126
|
end
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
match = apps.find { |name, _| name.start_with?(app_name) }
|
|
128
|
+
app = environment.app_matcher.find(app_name)
|
|
134
129
|
|
|
135
|
-
if
|
|
136
|
-
|
|
137
|
-
puts File.join(root, app_relative_path)
|
|
130
|
+
if app
|
|
131
|
+
puts File.join(root, app.relative_path)
|
|
138
132
|
else
|
|
139
133
|
puts "App '#{app_name}' not found"
|
|
140
134
|
end
|
data/bin/h-plugin
CHANGED
|
@@ -23,7 +23,8 @@ o.add_subcmd(:edit) { |*args|
|
|
|
23
23
|
if args.none?
|
|
24
24
|
system(ENV['EDITOR'] || 'safe_nvim', __FILE__)
|
|
25
25
|
else
|
|
26
|
-
|
|
26
|
+
pm = Hiiro::PrefixMatcher.new(plugin_files) { |f| File.basename(f) }
|
|
27
|
+
plugins = args.flat_map { |arg| pm.find_all(arg) }.uniq
|
|
27
28
|
|
|
28
29
|
if plugins.none?
|
|
29
30
|
puts "No matching plugins found for: #{args.map(&:inspect).join(' ')}"
|
data/exe/h
CHANGED
|
@@ -2,52 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
require "hiiro"
|
|
4
4
|
require "fileutils"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
plugin_files
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
bin_files.
|
|
45
|
-
|
|
46
|
-
|
|
5
|
+
require "pry"
|
|
6
|
+
|
|
7
|
+
Hiiro.run(*ARGV, cwd: Dir.pwd, plugins: [:Tasks]) do
|
|
8
|
+
add_subcommand(:version) { |*args|
|
|
9
|
+
puts Hiiro::VERSION
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
add_subcommand(:pry) { |*args|
|
|
13
|
+
binding.pry
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
add_subcommand(:ping) { |*args|
|
|
17
|
+
puts "pong"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
add_subcommand(:setup) do |*args|
|
|
21
|
+
gem_root = File.expand_path("../..", __FILE__)
|
|
22
|
+
source_plugins = File.join(gem_root, "plugins")
|
|
23
|
+
source_bins = File.join(gem_root, "bin")
|
|
24
|
+
dest_plugins = File.expand_path("~/.config/hiiro/plugins")
|
|
25
|
+
dest_bin = File.expand_path("~/bin")
|
|
26
|
+
|
|
27
|
+
FileUtils.mkdir_p(dest_plugins)
|
|
28
|
+
FileUtils.mkdir_p(dest_bin)
|
|
29
|
+
|
|
30
|
+
# Copy plugins
|
|
31
|
+
plugin_files = Dir["#{source_plugins}/*.rb"]
|
|
32
|
+
if plugin_files.any?
|
|
33
|
+
FileUtils.cp(plugin_files, dest_plugins)
|
|
34
|
+
puts "Installed #{plugin_files.size} plugins to #{dest_plugins}"
|
|
35
|
+
plugin_files.each { |f| puts " - #{File.basename(f)}" }
|
|
36
|
+
else
|
|
37
|
+
puts "No plugins found in #{source_plugins}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
puts
|
|
41
|
+
|
|
42
|
+
# Copy bin scripts
|
|
43
|
+
bin_files = Dir["#{source_bins}/h-*"]
|
|
44
|
+
if bin_files.any?
|
|
45
|
+
FileUtils.cp(bin_files, dest_bin)
|
|
46
|
+
bin_files.each { |f| FileUtils.chmod(0755, File.join(dest_bin, File.basename(f))) }
|
|
47
|
+
puts "Installed #{bin_files.size} subcommands to #{dest_bin}"
|
|
48
|
+
bin_files.each { |f| puts " - #{File.basename(f)}" }
|
|
49
|
+
else
|
|
50
|
+
puts "No subcommands found in #{source_bins}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
puts
|
|
54
|
+
puts "Setup complete! Make sure ~/bin is in your PATH."
|
|
47
55
|
end
|
|
48
|
-
|
|
49
|
-
puts
|
|
50
|
-
puts "Setup complete! Make sure ~/bin is in your PATH."
|
|
51
56
|
end
|
|
52
|
-
|
|
53
|
-
hiiro.run
|
data/lib/hiiro/history/entry.rb
CHANGED
|
@@ -85,12 +85,13 @@ class Hiiro
|
|
|
85
85
|
def oneline(index = nil)
|
|
86
86
|
time_str = timestamp ? Time.parse(timestamp).strftime('%m/%d %H:%M') : ''
|
|
87
87
|
prefix = index ? format('%3d ', index) : ''
|
|
88
|
+
sha_str = git_sha ? git_sha[0..6] : '-------'
|
|
88
89
|
branch_str = git_branch ? "[#{git_branch}]" : ''
|
|
89
90
|
task_str = task ? "(#{task})" : ''
|
|
90
91
|
cmd_str = cmd || description || ''
|
|
91
|
-
cmd_str = cmd_str[0..
|
|
92
|
+
cmd_str = cmd_str[0..35] + '...' if cmd_str.length > 38
|
|
92
93
|
|
|
93
|
-
"#{prefix}#{time_str} #{branch_str.ljust(20)} #{task_str.ljust(15)} #{cmd_str}"
|
|
94
|
+
"#{prefix}#{time_str} #{sha_str} #{branch_str.ljust(20)} #{task_str.ljust(15)} #{cmd_str}"
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
def full_display
|
data/lib/hiiro/history.rb
CHANGED
|
@@ -382,13 +382,13 @@ class Hiiro
|
|
|
382
382
|
end
|
|
383
383
|
|
|
384
384
|
def current_git_sha
|
|
385
|
-
git_helper.commit('HEAD'
|
|
385
|
+
git_helper.commit('HEAD')
|
|
386
386
|
end
|
|
387
387
|
|
|
388
388
|
def current_git_origin_sha
|
|
389
389
|
branch = current_git_branch
|
|
390
390
|
return nil unless branch
|
|
391
|
-
git_helper.commit("origin/#{branch}"
|
|
391
|
+
git_helper.commit("origin/#{branch}")
|
|
392
392
|
end
|
|
393
393
|
|
|
394
394
|
def current_git_worktree
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
class Hiiro
|
|
2
|
+
class PrefixMatcher
|
|
3
|
+
class << self
|
|
4
|
+
def find(items, prefix, key: nil, &block)
|
|
5
|
+
new(items, key, &block).find(prefix)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def find_all(items, prefix, key: nil, &block)
|
|
9
|
+
new(items, key, &block).find_all(prefix)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def resolve(items, prefix, key: nil, &block)
|
|
13
|
+
new(items, key, &block).resolve(prefix)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find_path(items, prefix, key: nil, &block)
|
|
17
|
+
new(items, key, &block).find_path(prefix)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def find_all_paths(items, prefix, key: nil, &block)
|
|
21
|
+
new(items, key, &block).find_all_paths(prefix)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def resolve_path(items, prefix, key: nil, &block)
|
|
25
|
+
new(items, key, &block).resolve_path(prefix)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :original_items, :key, :block
|
|
30
|
+
|
|
31
|
+
def initialize(items, key = nil, &block)
|
|
32
|
+
@original_items = items
|
|
33
|
+
@key = key
|
|
34
|
+
@block = block
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def items(key = nil, &block)
|
|
38
|
+
if key.nil? && !block_given?
|
|
39
|
+
@items ||= original_items.map { |item| extract(item, @key, &@block) }
|
|
40
|
+
else
|
|
41
|
+
original_items.map { |item| extract(item, key, &block) }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def extracted_items(key = nil, &block)
|
|
46
|
+
original_items.zip(items(key, &block))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def find(prefix, key = nil, &block)
|
|
50
|
+
extracted_items(key, &block).find { |_, extracted| matches?(extracted, prefix) }&.first
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def find_all(prefix, key = nil, &block)
|
|
54
|
+
extracted_items(key, &block).select { |_, extracted| matches?(extracted, prefix) }.map(&:first)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve(prefix, key = nil, &block)
|
|
58
|
+
pairs = extracted_items(key, &block)
|
|
59
|
+
|
|
60
|
+
exact = pairs.find { |_, extracted| extracted == prefix }
|
|
61
|
+
return exact.first if exact
|
|
62
|
+
|
|
63
|
+
matches = pairs.select { |_, extracted| matches?(extracted, prefix) }
|
|
64
|
+
matches.one? ? matches.first.first : nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def find_path(prefix, key = nil, &block)
|
|
68
|
+
matching_path_pairs(prefix, key, &block).first&.first
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def find_all_paths(prefix, key = nil, &block)
|
|
72
|
+
matching_path_pairs(prefix, key, &block).map(&:first)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resolve_path(prefix, key = nil, &block)
|
|
76
|
+
matches = matching_path_pairs(prefix, key, &block)
|
|
77
|
+
return nil if matches.empty?
|
|
78
|
+
return matches.first.first if matches.one?
|
|
79
|
+
|
|
80
|
+
exact = matches.find { |_, path| path == prefix }
|
|
81
|
+
exact&.first
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def matching_path_pairs(prefix, key = nil, &block)
|
|
87
|
+
prefixes = prefix.to_s.split('/')
|
|
88
|
+
|
|
89
|
+
pairs = extracted_items(key, &block).map { |item, extracted|
|
|
90
|
+
[item, extracted.to_s.split('/')]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
prefixes.each_with_index do |seg, i|
|
|
94
|
+
pairs = pairs.select { |_, path| path[i]&.start_with?(seg) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
pairs.map { |item, path| [item, path.join('/')] }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def matches?(item, prefix)
|
|
101
|
+
item.to_s.start_with?(prefix.to_s)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def extract(item, key = nil, &block)
|
|
105
|
+
return block.call(item) if block
|
|
106
|
+
return item.send(key) if key
|
|
107
|
+
item
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/hiiro/version.rb
CHANGED
data/lib/hiiro.rb
CHANGED
data/plugins/pins.rb
CHANGED
|
@@ -54,15 +54,11 @@ module Pins
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def find(partial)
|
|
57
|
-
pins.keys.map(&:to_s)
|
|
58
|
-
pin_name.start_with?(partial)
|
|
59
|
-
end
|
|
57
|
+
Hiiro::PrefixMatcher.find(pins.keys.map(&:to_s), partial)
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
def find_all(partial)
|
|
63
|
-
pins.keys.map(&:to_s)
|
|
64
|
-
pin_name.start_with?(partial)
|
|
65
|
-
end
|
|
61
|
+
Hiiro::PrefixMatcher.find_all(pins.keys.map(&:to_s), partial)
|
|
66
62
|
end
|
|
67
63
|
|
|
68
64
|
def remove(name)
|
data/plugins/tasks.rb
CHANGED
|
@@ -75,6 +75,8 @@ class Tree
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
class Task
|
|
78
|
+
HASH_METHODS = %i[name parent_name short_name session_name tree_name top_level? subtask?]
|
|
79
|
+
|
|
78
80
|
attr_reader :name, :tree_name, :session_name
|
|
79
81
|
|
|
80
82
|
def initialize(name:, tree: nil, session: nil, **_)
|
|
@@ -109,10 +111,7 @@ class Task
|
|
|
109
111
|
end
|
|
110
112
|
|
|
111
113
|
def to_h
|
|
112
|
-
|
|
113
|
-
h['tree'] = tree_name if tree_name
|
|
114
|
-
h['session'] = session_name if session_name != name
|
|
115
|
-
h
|
|
114
|
+
HASH_METHODS.zip(HASH_METHODS.map{|m| send(m) }).to_h
|
|
116
115
|
end
|
|
117
116
|
end
|
|
118
117
|
|
|
@@ -169,6 +168,22 @@ class Environment
|
|
|
169
168
|
@all_apps ||= config.apps
|
|
170
169
|
end
|
|
171
170
|
|
|
171
|
+
def tree_matcher
|
|
172
|
+
@tree_matcher ||= Hiiro::PrefixMatcher.new(all_trees, :name)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def session_matcher
|
|
176
|
+
@session_matcher ||= Hiiro::PrefixMatcher.new(all_sessions, :name)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def app_matcher
|
|
180
|
+
@app_matcher ||= Hiiro::PrefixMatcher.new(all_apps, :name)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def task_matcher
|
|
184
|
+
@task_matcher ||= Hiiro::PrefixMatcher.new(all_tasks, :name)
|
|
185
|
+
end
|
|
186
|
+
|
|
172
187
|
def task
|
|
173
188
|
@task ||= begin
|
|
174
189
|
s = session
|
|
@@ -191,36 +206,36 @@ class Environment
|
|
|
191
206
|
def find_task(abbreviated)
|
|
192
207
|
return nil if abbreviated.nil?
|
|
193
208
|
|
|
209
|
+
# Try path-based matching first (handles "parent/child" patterns)
|
|
194
210
|
if abbreviated.include?('/')
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return nil unless parent
|
|
198
|
-
|
|
199
|
-
subtask = all_tasks.select { |t| t.parent_name == parent.name }.find { |t| t.short_name.start_with?(child_prefix) }
|
|
200
|
-
return subtask if subtask
|
|
211
|
+
result = task_matcher.resolve_path(abbreviated)
|
|
212
|
+
return result if result
|
|
201
213
|
|
|
202
214
|
# "main" refers to the parent task itself
|
|
203
|
-
|
|
215
|
+
parent_prefix, child_prefix = abbreviated.split('/', 2)
|
|
216
|
+
if 'main'.start_with?(child_prefix)
|
|
217
|
+
return task_matcher.find(parent_prefix)
|
|
218
|
+
end
|
|
204
219
|
|
|
205
220
|
nil
|
|
206
221
|
else
|
|
207
|
-
|
|
222
|
+
task_matcher.find(abbreviated)
|
|
208
223
|
end
|
|
209
224
|
end
|
|
210
225
|
|
|
211
226
|
def find_tree(abbreviated)
|
|
212
227
|
return nil if abbreviated.nil?
|
|
213
|
-
|
|
228
|
+
tree_matcher.find(abbreviated)
|
|
214
229
|
end
|
|
215
230
|
|
|
216
231
|
def find_session(abbreviated)
|
|
217
232
|
return nil if abbreviated.nil?
|
|
218
|
-
|
|
233
|
+
session_matcher.find(abbreviated)
|
|
219
234
|
end
|
|
220
235
|
|
|
221
236
|
def find_app(abbreviated)
|
|
222
237
|
return nil if abbreviated.nil?
|
|
223
|
-
|
|
238
|
+
app_matcher.find(abbreviated)
|
|
224
239
|
end
|
|
225
240
|
end
|
|
226
241
|
|
|
@@ -228,89 +243,6 @@ class TaskManager
|
|
|
228
243
|
TASKS_DIR = File.join(Dir.home, '.config', 'hiiro', 'tasks')
|
|
229
244
|
APPS_FILE = File.join(Dir.home, '.config', 'hiiro', 'apps.yml')
|
|
230
245
|
|
|
231
|
-
class Config
|
|
232
|
-
attr_reader :tasks_file, :apps_file
|
|
233
|
-
|
|
234
|
-
def initialize(tasks_file: nil, apps_file: nil)
|
|
235
|
-
@tasks_file = tasks_file || File.join(TASKS_DIR, 'tasks.yml')
|
|
236
|
-
@apps_file = apps_file || APPS_FILE
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def tasks
|
|
240
|
-
data = load_tasks
|
|
241
|
-
(data['tasks'] || []).map { |h| Task.new(**h.transform_keys(&:to_sym)) }
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def apps
|
|
245
|
-
return [] unless File.exist?(apps_file)
|
|
246
|
-
data = YAML.safe_load_file(apps_file) || {}
|
|
247
|
-
data.map { |name, path| App.new(name: name, path: path) }
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def save_task(task)
|
|
251
|
-
data = load_tasks
|
|
252
|
-
data['tasks'] ||= []
|
|
253
|
-
data['tasks'].reject! { |t| t['name'] == task.name }
|
|
254
|
-
data['tasks'] << task.to_h
|
|
255
|
-
save_tasks(data)
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def remove_task(name)
|
|
259
|
-
data = load_tasks
|
|
260
|
-
data['tasks'] ||= []
|
|
261
|
-
data['tasks'].reject! { |t| t['name'] == name }
|
|
262
|
-
save_tasks(data)
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
private
|
|
266
|
-
|
|
267
|
-
def load_tasks
|
|
268
|
-
if File.exist?(tasks_file)
|
|
269
|
-
return YAML.safe_load_file(tasks_file) || { 'tasks' => [] }
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Load from individual task_*.yml files
|
|
273
|
-
task_files = Dir.glob(File.join(File.dirname(tasks_file), 'task_*.yml'))
|
|
274
|
-
if task_files.any?
|
|
275
|
-
tasks = task_files.map do |file|
|
|
276
|
-
short_name = File.basename(file, '.yml').sub(/^task_/, '')
|
|
277
|
-
data = YAML.safe_load_file(file) || {}
|
|
278
|
-
# Support parent key for subtasks, or infer from tree path
|
|
279
|
-
parent = data['parent']
|
|
280
|
-
if parent.nil? && data['tree']&.include?('/')
|
|
281
|
-
parent = data['tree'].split('/').first
|
|
282
|
-
end
|
|
283
|
-
name = parent ? "#{parent}/#{short_name}" : short_name
|
|
284
|
-
h = { 'name' => name }
|
|
285
|
-
h['tree'] = data['tree'] if data['tree']
|
|
286
|
-
h['session'] = data['session'] if data['session']
|
|
287
|
-
h
|
|
288
|
-
end
|
|
289
|
-
return { 'tasks' => tasks }
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
assignments_file = File.join(File.dirname(tasks_file), 'assignments.yml')
|
|
293
|
-
if File.exist?(assignments_file)
|
|
294
|
-
raw = YAML.safe_load_file(assignments_file) || {}
|
|
295
|
-
tasks = raw.map do |tree_path, task_name|
|
|
296
|
-
h = { 'name' => task_name, 'tree' => tree_path }
|
|
297
|
-
h['session'] = task_name if task_name.include?('/')
|
|
298
|
-
h
|
|
299
|
-
end
|
|
300
|
-
data = { 'tasks' => tasks }
|
|
301
|
-
save_tasks(data)
|
|
302
|
-
return data
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
{ 'tasks' => [] }
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def save_tasks(data)
|
|
309
|
-
FileUtils.mkdir_p(File.dirname(tasks_file))
|
|
310
|
-
File.write(tasks_file, YAML.dump(data))
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
|
|
314
246
|
attr_reader :hiiro, :scope, :environment
|
|
315
247
|
|
|
316
248
|
def initialize(hiiro, scope: :task, environment: nil)
|
|
@@ -344,18 +276,16 @@ class TaskManager
|
|
|
344
276
|
def task_by_name(name)
|
|
345
277
|
return slash_lookup(name) if name.include?('/')
|
|
346
278
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
match_name.start_with?(name)
|
|
350
|
-
}
|
|
279
|
+
key = (scope == :subtask) ? :short_name : :name
|
|
280
|
+
Hiiro::PrefixMatcher.new(tasks, key).find(name)
|
|
351
281
|
end
|
|
352
282
|
|
|
353
283
|
def task_by_tree(tree_name)
|
|
354
|
-
|
|
284
|
+
environment.task_matcher.resolve(tree_name, :tree_name)
|
|
355
285
|
end
|
|
356
286
|
|
|
357
287
|
def task_by_session(session_name)
|
|
358
|
-
|
|
288
|
+
environment.task_matcher.resolve(session_name, :session_name)
|
|
359
289
|
end
|
|
360
290
|
|
|
361
291
|
def current_task
|
|
@@ -619,7 +549,7 @@ class TaskManager
|
|
|
619
549
|
return
|
|
620
550
|
end
|
|
621
551
|
|
|
622
|
-
matches = environment.
|
|
552
|
+
matches = environment.app_matcher.find_all(app_name)
|
|
623
553
|
|
|
624
554
|
case matches.count
|
|
625
555
|
when 0
|
|
@@ -693,7 +623,7 @@ class TaskManager
|
|
|
693
623
|
tree = environment.find_tree(task.tree_name)
|
|
694
624
|
tree_root = tree ? tree.path : File.join(WORK_DIR, task.tree_name)
|
|
695
625
|
|
|
696
|
-
matches = environment.
|
|
626
|
+
matches = environment.app_matcher.find_all(app_name)
|
|
697
627
|
|
|
698
628
|
case matches.count
|
|
699
629
|
when 0
|
|
@@ -742,14 +672,106 @@ class TaskManager
|
|
|
742
672
|
def sk_select(items)
|
|
743
673
|
Hiiro::Sk.select(items)
|
|
744
674
|
end
|
|
675
|
+
|
|
676
|
+
class Config
|
|
677
|
+
attr_reader :tasks_file, :apps_file
|
|
678
|
+
|
|
679
|
+
def initialize(tasks_file: nil, apps_file: nil)
|
|
680
|
+
@tasks_file = tasks_file || File.join(TASKS_DIR, 'tasks.yml')
|
|
681
|
+
@apps_file = apps_file || APPS_FILE
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def tasks
|
|
685
|
+
data = load_tasks
|
|
686
|
+
(data['tasks'] || []).map { |h| Task.new(**h.transform_keys(&:to_sym)) }
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def apps
|
|
690
|
+
return [] unless File.exist?(apps_file)
|
|
691
|
+
data = YAML.safe_load_file(apps_file) || {}
|
|
692
|
+
data.map { |name, path| App.new(name: name, path: path) }
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
def save_task(task)
|
|
696
|
+
data = load_tasks
|
|
697
|
+
data['tasks'] ||= []
|
|
698
|
+
data['tasks'].reject! { |t| t['name'] == task.name }
|
|
699
|
+
data['tasks'] << task.to_h
|
|
700
|
+
save_tasks(data)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
def remove_task(name)
|
|
704
|
+
data = load_tasks
|
|
705
|
+
data['tasks'] ||= []
|
|
706
|
+
data['tasks'].reject! { |t| t['name'] == name }
|
|
707
|
+
save_tasks(data)
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
private
|
|
711
|
+
|
|
712
|
+
def load_tasks
|
|
713
|
+
if File.exist?(tasks_file)
|
|
714
|
+
return YAML.safe_load_file(tasks_file) || { 'tasks' => [] }
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# Load from individual task_*.yml files
|
|
718
|
+
task_files = Dir.glob(File.join(File.dirname(tasks_file), 'task_*.yml'))
|
|
719
|
+
if task_files.any?
|
|
720
|
+
tasks = task_files.map do |file|
|
|
721
|
+
short_name = File.basename(file, '.yml').sub(/^task_/, '')
|
|
722
|
+
data = YAML.safe_load_file(file) || {}
|
|
723
|
+
# Support parent key for subtasks, or infer from tree path
|
|
724
|
+
parent = data['parent']
|
|
725
|
+
if parent.nil? && data['tree']&.include?('/')
|
|
726
|
+
parent = data['tree'].split('/').first
|
|
727
|
+
end
|
|
728
|
+
name = parent ? "#{parent}/#{short_name}" : short_name
|
|
729
|
+
h = { 'name' => name }
|
|
730
|
+
h['tree'] = data['tree'] if data['tree']
|
|
731
|
+
h['session'] = data['session'] if data['session']
|
|
732
|
+
h
|
|
733
|
+
end
|
|
734
|
+
return { 'tasks' => tasks }
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
assignments_file = File.join(File.dirname(tasks_file), 'assignments.yml')
|
|
738
|
+
if File.exist?(assignments_file)
|
|
739
|
+
raw = YAML.safe_load_file(assignments_file) || {}
|
|
740
|
+
tasks = raw.map do |tree_path, task_name|
|
|
741
|
+
h = { 'name' => task_name, 'tree' => tree_path }
|
|
742
|
+
h['session'] = task_name if task_name.include?('/')
|
|
743
|
+
h
|
|
744
|
+
end
|
|
745
|
+
data = { 'tasks' => tasks }
|
|
746
|
+
save_tasks(data)
|
|
747
|
+
return data
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
{ 'tasks' => [] }
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def save_tasks(data)
|
|
754
|
+
FileUtils.mkdir_p(File.dirname(tasks_file))
|
|
755
|
+
File.write(tasks_file, YAML.dump(data))
|
|
756
|
+
end
|
|
757
|
+
end
|
|
745
758
|
end
|
|
746
759
|
|
|
747
760
|
module Tasks
|
|
748
761
|
def self.load(hiiro)
|
|
749
762
|
hiiro.load_plugin(Tmux)
|
|
763
|
+
attach_methods(hiiro)
|
|
750
764
|
add_subcommands(hiiro)
|
|
751
765
|
end
|
|
752
766
|
|
|
767
|
+
def self.attach_methods(hiiro)
|
|
768
|
+
hiiro.instance_eval do
|
|
769
|
+
def environment
|
|
770
|
+
@environment ||= Environment.current
|
|
771
|
+
end
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
753
775
|
def self.add_subcommands(hiiro)
|
|
754
776
|
hiiro.add_subcmd(:task) do |*args|
|
|
755
777
|
mgr = TaskManager.new(hiiro, scope: :task)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hiiro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.56
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Toyota
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pry
|
|
@@ -79,6 +79,7 @@ files:
|
|
|
79
79
|
- lib/hiiro/history.rb
|
|
80
80
|
- lib/hiiro/history/entry.rb
|
|
81
81
|
- lib/hiiro/options.rb
|
|
82
|
+
- lib/hiiro/prefix_matcher.rb
|
|
82
83
|
- lib/hiiro/sk.rb
|
|
83
84
|
- lib/hiiro/version.rb
|
|
84
85
|
- notes
|