hiiro 0.1.15 → 0.1.17
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-link +20 -1
- data/lib/hiiro/history.rb +268 -0
- data/lib/hiiro/version.rb +1 -1
- data/lib/hiiro.rb +25 -4
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30801e00dc876dec391ff411a4f20a500ca411e7d2ebf77318790ace5fc603bc
|
|
4
|
+
data.tar.gz: e6bae4d3c68e5edf720f1eeb0b282f9c15ca1c662ff460eb2bae25b38dd30904
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74879d93880ef6c1ed72777ca0fbabe19273d22d2acc7f42ddcbb12a80db439ae0a0a09b7b4970483235b591c69e71e6dbb1015cac3d10364bff2e794fd0a7e9
|
|
7
|
+
data.tar.gz: 35c0a0a4f90e2fb22bd3129131ad8904c12ba3d528a7e6a0d2568656767c97620235c7e4d6180d968a5ab3926fdb104cff5a9230416ef94a79ed76e598276a73
|
data/bin/h-link
CHANGED
|
@@ -39,9 +39,15 @@ def hash_matches?(links, *args)
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def
|
|
42
|
+
def search_links(links, *args)
|
|
43
43
|
terms = args.map(&:downcase)
|
|
44
44
|
|
|
45
|
+
links.select do |link|
|
|
46
|
+
link_matches?(link, *terms)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def link_matches?(link, *terms)
|
|
45
51
|
searchable = [
|
|
46
52
|
link['url'],
|
|
47
53
|
link['description'],
|
|
@@ -234,6 +240,8 @@ o.add_subcmd(:open) do |*args|
|
|
|
234
240
|
if args.empty?
|
|
235
241
|
lines = load_link_hash(links)
|
|
236
242
|
|
|
243
|
+
exit_code = 1
|
|
244
|
+
|
|
237
245
|
if args.any?
|
|
238
246
|
lines = hash_matches?(lines, *args)
|
|
239
247
|
end
|
|
@@ -244,12 +252,23 @@ o.add_subcmd(:open) do |*args|
|
|
|
244
252
|
if status.success? && !selected.strip.empty?
|
|
245
253
|
if selected =~ /^\s*(\d+)\.\s+(\S+)/
|
|
246
254
|
system('open', $2)
|
|
255
|
+
exit_code = 0
|
|
247
256
|
end
|
|
248
257
|
end
|
|
258
|
+
|
|
259
|
+
exit exit_code
|
|
249
260
|
end
|
|
250
261
|
|
|
251
262
|
idx, link = find_link_by_ref(args.first, links)
|
|
252
263
|
|
|
264
|
+
if link.nil?
|
|
265
|
+
matches = search_links(links, *args)
|
|
266
|
+
|
|
267
|
+
if matches.count == 1
|
|
268
|
+
link = matches.first
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
253
272
|
if link.nil?
|
|
254
273
|
puts "Link not found: #{args.first}"
|
|
255
274
|
exit 1
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'time'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
class Hiiro
|
|
6
|
+
class History
|
|
7
|
+
HISTORY_FILE = File.join(Dir.home, '.config/hiiro/history.yml')
|
|
8
|
+
|
|
9
|
+
class Entry
|
|
10
|
+
attr_reader :id, :timestamp, :source, :cmd, :description
|
|
11
|
+
attr_reader :tmux_session, :tmux_window, :tmux_pane
|
|
12
|
+
attr_reader :git_branch, :git_worktree
|
|
13
|
+
attr_reader :task, :subtask
|
|
14
|
+
|
|
15
|
+
def initialize(data)
|
|
16
|
+
@id = data['id']
|
|
17
|
+
@timestamp = data['timestamp']
|
|
18
|
+
@source = data['source']
|
|
19
|
+
@cmd = data['cmd']
|
|
20
|
+
@description = data['description']
|
|
21
|
+
@tmux_session = data['tmux_session']
|
|
22
|
+
@tmux_window = data['tmux_window']
|
|
23
|
+
@tmux_pane = data['tmux_pane']
|
|
24
|
+
@git_branch = data['git_branch']
|
|
25
|
+
@git_worktree = data['git_worktree']
|
|
26
|
+
@task = data['task']
|
|
27
|
+
@subtask = data['subtask']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_h
|
|
31
|
+
{
|
|
32
|
+
'id' => id,
|
|
33
|
+
'timestamp' => timestamp,
|
|
34
|
+
'source' => source,
|
|
35
|
+
'cmd' => cmd,
|
|
36
|
+
'description' => description,
|
|
37
|
+
'tmux_session' => tmux_session,
|
|
38
|
+
'tmux_window' => tmux_window,
|
|
39
|
+
'tmux_pane' => tmux_pane,
|
|
40
|
+
'git_branch' => git_branch,
|
|
41
|
+
'git_worktree' => git_worktree,
|
|
42
|
+
'task' => task,
|
|
43
|
+
'subtask' => subtask,
|
|
44
|
+
}.compact
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def short_line(index)
|
|
48
|
+
time_str = Time.parse(timestamp).strftime('%Y-%m-%d %H:%M')
|
|
49
|
+
desc = description || cmd || '(no description)'
|
|
50
|
+
desc = desc[0..60] + '...' if desc.length > 63
|
|
51
|
+
format('%3d %s %s', index, time_str, desc)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def full_display
|
|
55
|
+
lines = []
|
|
56
|
+
lines << "ID: #{id}"
|
|
57
|
+
lines << "Timestamp: #{timestamp}"
|
|
58
|
+
lines << "Description: #{description}" if description
|
|
59
|
+
lines << "Command: #{cmd}" if cmd
|
|
60
|
+
lines << "Source: #{source}" if source
|
|
61
|
+
lines << ""
|
|
62
|
+
lines << "Tmux:"
|
|
63
|
+
lines << " Session: #{tmux_session}" if tmux_session
|
|
64
|
+
lines << " Window: #{tmux_window}" if tmux_window
|
|
65
|
+
lines << " Pane: #{tmux_pane}" if tmux_pane
|
|
66
|
+
lines << ""
|
|
67
|
+
lines << "Git:"
|
|
68
|
+
lines << " Branch: #{git_branch}" if git_branch
|
|
69
|
+
lines << " Worktree: #{git_worktree}" if git_worktree
|
|
70
|
+
lines << ""
|
|
71
|
+
lines << "Task:"
|
|
72
|
+
lines << " Task: #{task}" if task
|
|
73
|
+
lines << " Subtask: #{subtask}" if subtask
|
|
74
|
+
lines.join("\n")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class << self
|
|
79
|
+
def load(hiiro)
|
|
80
|
+
history = new
|
|
81
|
+
|
|
82
|
+
hiiro.add_subcmd(:history) do |*args|
|
|
83
|
+
subcmd, *rest = args
|
|
84
|
+
case subcmd
|
|
85
|
+
when 'ls', 'list', nil
|
|
86
|
+
history.list
|
|
87
|
+
when 'show'
|
|
88
|
+
history.show(rest.first)
|
|
89
|
+
when 'goto'
|
|
90
|
+
history.goto(rest.first)
|
|
91
|
+
when 'add'
|
|
92
|
+
history.add_manual(rest.join(' '), hiiro: hiiro)
|
|
93
|
+
else
|
|
94
|
+
puts "Unknown history subcommand: #{subcmd}"
|
|
95
|
+
puts "Available: ls, show <id>, goto <id>, add <description>"
|
|
96
|
+
false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def log(description: nil, pwd: Dir.pwd, cmd: nil, source: nil, task: nil, subtask: nil)
|
|
102
|
+
new.add(
|
|
103
|
+
description: description,
|
|
104
|
+
cmd: cmd,
|
|
105
|
+
pwd: pwd,
|
|
106
|
+
source: source,
|
|
107
|
+
task: task,
|
|
108
|
+
subtask: subtask
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def initialize
|
|
114
|
+
ensure_history_file
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def entries
|
|
118
|
+
@entries ||= load_entries
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def reload
|
|
122
|
+
@entries = nil
|
|
123
|
+
entries
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def list
|
|
127
|
+
if entries.empty?
|
|
128
|
+
puts "No history entries."
|
|
129
|
+
return true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
entries.each_with_index do |entry, idx|
|
|
133
|
+
puts entry.short_line(idx + 1)
|
|
134
|
+
end
|
|
135
|
+
true
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def show(ref)
|
|
139
|
+
entry = find_entry(ref)
|
|
140
|
+
unless entry
|
|
141
|
+
puts "Entry not found: #{ref}"
|
|
142
|
+
return false
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
puts entry.full_display
|
|
146
|
+
true
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def goto(ref)
|
|
150
|
+
entry = find_entry(ref)
|
|
151
|
+
unless entry
|
|
152
|
+
puts "Entry not found: #{ref}"
|
|
153
|
+
return false
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
unless entry.tmux_session
|
|
157
|
+
puts "No tmux session recorded for this entry"
|
|
158
|
+
return false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
target = entry.tmux_session
|
|
162
|
+
target += ":#{entry.tmux_window}" if entry.tmux_window
|
|
163
|
+
target += ".#{entry.tmux_pane}" if entry.tmux_pane
|
|
164
|
+
|
|
165
|
+
system('tmux', 'switch-client', '-t', target)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def add_manual(description, hiiro: nil)
|
|
169
|
+
add(description: description, source: 'manual')
|
|
170
|
+
puts "Added history entry: #{description}"
|
|
171
|
+
true
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def add(description: nil, cmd: nil, source: nil, task: nil, subtask: nil, pwd: Dir.pwd)
|
|
175
|
+
cmd ||=
|
|
176
|
+
entry_data = {
|
|
177
|
+
'id' => generate_id,
|
|
178
|
+
'timestamp' => Time.now.iso8601,
|
|
179
|
+
'description' => description,
|
|
180
|
+
'cmd' => cmd,
|
|
181
|
+
'pwd' => pwd,
|
|
182
|
+
'source' => source,
|
|
183
|
+
'tmux_session' => current_tmux_session,
|
|
184
|
+
'tmux_window' => current_tmux_window,
|
|
185
|
+
'tmux_pane' => current_tmux_pane,
|
|
186
|
+
'git_branch' => current_git_branch,
|
|
187
|
+
'git_worktree' => current_git_worktree,
|
|
188
|
+
'task' => task,
|
|
189
|
+
'subtask' => subtask,
|
|
190
|
+
}.compact
|
|
191
|
+
|
|
192
|
+
all = load_raw_entries
|
|
193
|
+
all << entry_data
|
|
194
|
+
save_entries(all)
|
|
195
|
+
|
|
196
|
+
Entry.new(entry_data)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
|
|
201
|
+
def find_entry(ref)
|
|
202
|
+
return nil unless ref
|
|
203
|
+
|
|
204
|
+
if ref.to_s.match?(/^\d+$/)
|
|
205
|
+
idx = ref.to_i - 1
|
|
206
|
+
return entries[idx] if idx >= 0 && idx < entries.length
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
entries.find { |e| e.id == ref.to_s }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def generate_id
|
|
213
|
+
Time.now.strftime('%Y%m%d%H%M%S') + '-' + rand(10000).to_s.rjust(4, '0')
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def ensure_history_file
|
|
217
|
+
dir = File.dirname(HISTORY_FILE)
|
|
218
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
|
219
|
+
|
|
220
|
+
unless File.exist?(HISTORY_FILE)
|
|
221
|
+
File.write(HISTORY_FILE, [].to_yaml)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def load_raw_entries
|
|
226
|
+
YAML.load_file(HISTORY_FILE) || []
|
|
227
|
+
rescue => e
|
|
228
|
+
[]
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def load_entries
|
|
232
|
+
load_raw_entries.map { |data| Entry.new(data) }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def save_entries(entries_data)
|
|
236
|
+
File.write(HISTORY_FILE, entries_data.to_yaml)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def current_tmux_session
|
|
240
|
+
return nil unless ENV['TMUX']
|
|
241
|
+
`tmux display-message -p '#S'`.strip rescue nil
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def current_tmux_window
|
|
245
|
+
return nil unless ENV['TMUX']
|
|
246
|
+
`tmux display-message -p '#I'`.strip rescue nil
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def current_tmux_pane
|
|
250
|
+
return nil unless ENV['TMUX']
|
|
251
|
+
`tmux display-message -p '#P'`.strip rescue nil
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def current_git_branch
|
|
255
|
+
branch = `git branch --show-current 2>/dev/null`.strip
|
|
256
|
+
branch.empty? ? nil : branch
|
|
257
|
+
rescue
|
|
258
|
+
nil
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def current_git_worktree
|
|
262
|
+
worktree = `git rev-parse --show-toplevel 2>/dev/null`.strip
|
|
263
|
+
worktree.empty? ? nil : worktree
|
|
264
|
+
rescue
|
|
265
|
+
nil
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
data/lib/hiiro/version.rb
CHANGED
data/lib/hiiro.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
require "fileutils"
|
|
2
2
|
require "yaml"
|
|
3
|
+
require "shellwords"
|
|
3
4
|
|
|
4
5
|
require_relative "hiiro/version"
|
|
6
|
+
require_relative "hiiro/history"
|
|
5
7
|
|
|
6
8
|
class Hiiro
|
|
7
9
|
def self.init(*args, plugins: [], logging: false, **values, &block)
|
|
@@ -9,6 +11,7 @@ class Hiiro
|
|
|
9
11
|
args = ARGV if args.empty?
|
|
10
12
|
|
|
11
13
|
new($0, *args, logging: logging, **values).tap do |hiiro|
|
|
14
|
+
History.load(hiiro)
|
|
12
15
|
hiiro.load_plugins(*plugins)
|
|
13
16
|
|
|
14
17
|
hiiro.add_subcmd(:edit, **values) { |*args|
|
|
@@ -33,23 +36,32 @@ class Hiiro
|
|
|
33
36
|
self
|
|
34
37
|
end
|
|
35
38
|
|
|
36
|
-
attr_reader :bin, :bin_name, :all_args
|
|
39
|
+
attr_reader :bin, :bin_name, :all_args, :full_command
|
|
37
40
|
attr_reader :subcmd, :args
|
|
38
41
|
attr_reader :loaded_plugins
|
|
39
42
|
attr_reader :logging
|
|
40
43
|
attr_reader :global_values
|
|
41
44
|
|
|
42
|
-
def initialize(bin, *
|
|
45
|
+
def initialize(bin, *all_args, logging: false, **values)
|
|
43
46
|
@bin = bin
|
|
44
47
|
@bin_name = File.basename(bin)
|
|
45
|
-
@all_args =
|
|
46
|
-
@subcmd, *@args =
|
|
48
|
+
@all_args = all_args
|
|
49
|
+
@subcmd, *@args = all_args # normally i would never do this
|
|
47
50
|
@loaded_plugins = []
|
|
48
51
|
@logging = logging
|
|
49
52
|
@global_values = values
|
|
53
|
+
@full_command = [
|
|
54
|
+
bin,
|
|
55
|
+
all_args,
|
|
56
|
+
].map(&:shellescape).join(' ')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def history
|
|
60
|
+
@history ||= History.new
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
def run
|
|
64
|
+
log(description: 'hiiro: ran command')
|
|
53
65
|
result = runner.run(*args)
|
|
54
66
|
|
|
55
67
|
handle_result(result)
|
|
@@ -61,6 +73,15 @@ class Hiiro
|
|
|
61
73
|
exit 1
|
|
62
74
|
end
|
|
63
75
|
|
|
76
|
+
def log(**opts)
|
|
77
|
+
history.add(
|
|
78
|
+
description: 'command ran',
|
|
79
|
+
cmd: full_command,
|
|
80
|
+
source: 'hiiro',
|
|
81
|
+
**opts,
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
64
85
|
def handle_result(result)
|
|
65
86
|
exit 0 if result.nil? || result
|
|
66
87
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Toyota
|
|
@@ -61,6 +61,7 @@ files:
|
|
|
61
61
|
- exe/h
|
|
62
62
|
- hiiro.gemspec
|
|
63
63
|
- lib/hiiro.rb
|
|
64
|
+
- lib/hiiro/history.rb
|
|
64
65
|
- lib/hiiro/version.rb
|
|
65
66
|
- links.backup.yml
|
|
66
67
|
- plugins/notify.rb
|