hiiro 0.1.16 → 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/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
|
|
@@ -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
|