hiiro 0.1.201 → 0.1.203
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-pr +94 -9
- data/lib/hiiro/queue.rb +60 -40
- data/lib/hiiro/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c0bb6c6511776861d3adb6a22dbc07343bade8517cdbef483dd6696e2695911
|
|
4
|
+
data.tar.gz: 6367f7818b192365e44b7109d1199583c07f23bd3a08890019b26cc3a0e5dc5b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3fe17a443379a9aec58acbd7f9f664cc363c508dbb87014f964ee4c4e1dd12e3c9be3236e7af7d8e96116ada68eb28018be1337da355d8d2a04709a948dd3a04
|
|
7
|
+
data.tar.gz: 8a731d6af3505960bd6683b5b439cb60cc839a54cdc307ebe9decb989e84b23860f7a50d244edb7dbd29857b9e4742548c153c6758d5fa85272b82c55820d2e1
|
data/bin/h-pr
CHANGED
|
@@ -195,7 +195,17 @@ class PinnedPRManager
|
|
|
195
195
|
|
|
196
196
|
def load_pinned
|
|
197
197
|
ensure_file
|
|
198
|
-
YAML.load_file(PINNED_FILE) || []
|
|
198
|
+
prs = YAML.load_file(PINNED_FILE) || []
|
|
199
|
+
if prs.any? { |p| !p['slot'] }
|
|
200
|
+
next_slot = prs.map { |p| p['slot'].to_i }.max.to_i
|
|
201
|
+
prs.each do |p|
|
|
202
|
+
next if p['slot']
|
|
203
|
+
next_slot += 1
|
|
204
|
+
p['slot'] = next_slot
|
|
205
|
+
end
|
|
206
|
+
save_pinned(prs)
|
|
207
|
+
end
|
|
208
|
+
prs
|
|
199
209
|
end
|
|
200
210
|
|
|
201
211
|
def save_pinned(prs)
|
|
@@ -215,6 +225,7 @@ class PinnedPRManager
|
|
|
215
225
|
existing.merge!(pr_info)
|
|
216
226
|
existing['updated_at'] = Time.now.iso8601
|
|
217
227
|
else
|
|
228
|
+
pr_info['slot'] = (pinned.map { |p| p['slot'].to_i }.max.to_i) + 1
|
|
218
229
|
pr_info['pinned_at'] = Time.now.iso8601
|
|
219
230
|
pinned << pr_info
|
|
220
231
|
end
|
|
@@ -485,7 +496,7 @@ class PinnedPRManager
|
|
|
485
496
|
end
|
|
486
497
|
|
|
487
498
|
def display_pinned(pr, idx = nil)
|
|
488
|
-
num = idx ? "#{(idx + 1).to_s.rjust(3)}." : ""
|
|
499
|
+
num = idx ? "#{(pr['slot'] || idx + 1).to_s.rjust(3)}." : ""
|
|
489
500
|
|
|
490
501
|
check_emoji = if pr['checks']
|
|
491
502
|
c = pr['checks']
|
|
@@ -634,7 +645,11 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
634
645
|
end
|
|
635
646
|
|
|
636
647
|
if ref.to_s =~ /^\d+$/ && ref.to_i > 0
|
|
637
|
-
|
|
648
|
+
slot = ref.to_i
|
|
649
|
+
by_slot = pinned.find { |p| p['slot'].to_i == slot }
|
|
650
|
+
return by_slot['number'].to_s if by_slot
|
|
651
|
+
# Fall back to 1-based index for unslotted data
|
|
652
|
+
idx = slot - 1
|
|
638
653
|
return pinned[idx]['number'].to_s if idx < pinned.length
|
|
639
654
|
end
|
|
640
655
|
|
|
@@ -688,15 +703,23 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
688
703
|
end
|
|
689
704
|
}
|
|
690
705
|
|
|
691
|
-
add_subcmd(:link) {
|
|
692
|
-
|
|
706
|
+
add_subcmd(:link) { |ref=nil|
|
|
707
|
+
pr_number = resolve_pr.call(ref)
|
|
708
|
+
next unless pr_number
|
|
693
709
|
|
|
694
|
-
|
|
710
|
+
pinned = pinned_manager.load_pinned
|
|
711
|
+
pr = pinned.find { |p| p['number'].to_s == pr_number.to_s }
|
|
695
712
|
|
|
696
|
-
if
|
|
697
|
-
print ['
|
|
713
|
+
if pr && pr['url']
|
|
714
|
+
print pr['url']
|
|
698
715
|
else
|
|
699
|
-
|
|
716
|
+
output = `gh pr view #{pr_number} --json url 2>/dev/null`
|
|
717
|
+
info = JSON.parse(output) rescue nil
|
|
718
|
+
if info&.dig('url')
|
|
719
|
+
print info['url']
|
|
720
|
+
else
|
|
721
|
+
STDERR.puts "Could not find URL for PR ##{pr_number}"
|
|
722
|
+
end
|
|
700
723
|
end
|
|
701
724
|
}
|
|
702
725
|
|
|
@@ -1127,6 +1150,68 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1127
1150
|
puts "Added #{added} PR(s) to tracking."
|
|
1128
1151
|
end
|
|
1129
1152
|
|
|
1153
|
+
add_subcmd(:attach) do |ref = nil|
|
|
1154
|
+
pr_number = resolve_pr.call(ref)
|
|
1155
|
+
next unless pr_number
|
|
1156
|
+
|
|
1157
|
+
pinned = pinned_manager.load_pinned
|
|
1158
|
+
pr = pinned.find { |p| p['number'].to_s == pr_number.to_s }
|
|
1159
|
+
|
|
1160
|
+
unless pr
|
|
1161
|
+
puts "PR ##{pr_number} not in pinned list — run 'h pr update' first"
|
|
1162
|
+
next
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
branch = pr['headRefName']
|
|
1166
|
+
task_name = pr['task']
|
|
1167
|
+
|
|
1168
|
+
unless branch
|
|
1169
|
+
puts "PR ##{pr_number} has no branch info"
|
|
1170
|
+
next
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1173
|
+
# Resolve task session and working directory
|
|
1174
|
+
target_session = nil
|
|
1175
|
+
working_dir = Dir.pwd
|
|
1176
|
+
|
|
1177
|
+
if task_name
|
|
1178
|
+
env = Environment.current rescue nil
|
|
1179
|
+
task = env&.find_task(task_name)
|
|
1180
|
+
if task
|
|
1181
|
+
target_session = task.session_name
|
|
1182
|
+
tree = task.tree
|
|
1183
|
+
working_dir = tree.path if tree
|
|
1184
|
+
end
|
|
1185
|
+
end
|
|
1186
|
+
|
|
1187
|
+
unless target_session && system('tmux', 'has-session', '-t', target_session, out: File::NULL, err: File::NULL)
|
|
1188
|
+
puts "Task '#{task_name || '(none)'}' has no active tmux session"
|
|
1189
|
+
next
|
|
1190
|
+
end
|
|
1191
|
+
|
|
1192
|
+
# Write a launcher: commit WIP if dirty, checkout the PR branch, then drop into shell
|
|
1193
|
+
script_path = "/tmp/h-pr-attach-#{pr_number}.sh"
|
|
1194
|
+
File.write(script_path, <<~SH)
|
|
1195
|
+
#!/usr/bin/env bash
|
|
1196
|
+
cd #{Shellwords.shellescape(working_dir)}
|
|
1197
|
+
|
|
1198
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
1199
|
+
git add -A
|
|
1200
|
+
git commit -m "WIP"
|
|
1201
|
+
echo ""
|
|
1202
|
+
echo "WIP COMMIT: ALL UNCOMMITTED CHANGES HAVE BEEN COMMITTED BEFORE SWITCHING BRANCHES"
|
|
1203
|
+
echo ""
|
|
1204
|
+
fi
|
|
1205
|
+
|
|
1206
|
+
git checkout #{Shellwords.shellescape(branch)}
|
|
1207
|
+
exec #{Shellwords.shellescape(ENV['SHELL'] || 'zsh')}
|
|
1208
|
+
SH
|
|
1209
|
+
FileUtils.chmod(0755, script_path)
|
|
1210
|
+
|
|
1211
|
+
system('tmux', 'new-window', '-t', target_session, '-c', working_dir, script_path)
|
|
1212
|
+
system('tmux', 'switch-client', '-t', target_session)
|
|
1213
|
+
end
|
|
1214
|
+
|
|
1130
1215
|
add_subcmd(:track) do
|
|
1131
1216
|
pr_info = pinned_manager.fetch_current_branch_pr
|
|
1132
1217
|
unless pr_info
|
data/lib/hiiro/queue.rb
CHANGED
|
@@ -40,6 +40,50 @@ class Hiiro
|
|
|
40
40
|
Dir.glob(File.join(dir, '*.md')).sort.map { |f| File.basename(f, '.md') }
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
def tasks_in_sorted(status)
|
|
44
|
+
dir = queue_dirs[status]
|
|
45
|
+
Dir.glob(File.join(dir, '*.md')).map { |f|
|
|
46
|
+
{ name: File.basename(f, '.md'), mtime: File.mtime(f) }
|
|
47
|
+
}.sort_by { |t| -t[:mtime].to_i }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def format_mtime(mtime)
|
|
51
|
+
now = Time.now
|
|
52
|
+
mtime.year == now.year ? mtime.strftime("%m-%d %H:%M") : mtime.strftime("%Y-%m-%d %H:%M")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def list_lines(all: false)
|
|
56
|
+
lines = []
|
|
57
|
+
STATUSES.each do |status|
|
|
58
|
+
tasks = tasks_in_sorted(status.to_sym)
|
|
59
|
+
next if tasks.empty?
|
|
60
|
+
|
|
61
|
+
display = all ? tasks : tasks.first(10)
|
|
62
|
+
display.each do |t|
|
|
63
|
+
ts = format_mtime(t[:mtime])
|
|
64
|
+
line = "%-10s %-12s %s" % [status, ts, t[:name]]
|
|
65
|
+
meta = meta_for(t[:name], status.to_sym)
|
|
66
|
+
if meta && status == 'running'
|
|
67
|
+
started = meta['started_at']
|
|
68
|
+
if started
|
|
69
|
+
elapsed = Time.now - Time.parse(started)
|
|
70
|
+
mins = (elapsed / 60).to_i
|
|
71
|
+
line += " (#{mins}m)"
|
|
72
|
+
end
|
|
73
|
+
line += " [#{meta['tmux_session']}:#{meta['tmux_window']}]" if meta['tmux_session']
|
|
74
|
+
end
|
|
75
|
+
preview = task_preview(t[:name], status.to_sym)
|
|
76
|
+
line += " #{preview}" if preview
|
|
77
|
+
lines << line
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if !all && tasks.size > 10
|
|
81
|
+
lines << " ... and #{tasks.size - 10} more"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
lines
|
|
85
|
+
end
|
|
86
|
+
|
|
43
87
|
def all_tasks
|
|
44
88
|
STATUSES.flat_map do |status|
|
|
45
89
|
tasks_in(status.to_sym).map { |name| { name: name, status: status } }
|
|
@@ -315,54 +359,30 @@ class Hiiro
|
|
|
315
359
|
end
|
|
316
360
|
}
|
|
317
361
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
puts "No tasks"
|
|
322
|
-
next
|
|
323
|
-
end
|
|
324
|
-
tasks.each do |t|
|
|
325
|
-
line = "%-10s %s" % [t[:status], t[:name]]
|
|
326
|
-
meta = q.meta_for(t[:name], t[:status].to_sym)
|
|
327
|
-
if meta && t[:status] == 'running'
|
|
328
|
-
started = meta['started_at']
|
|
329
|
-
if started
|
|
330
|
-
elapsed = Time.now - Time.parse(started)
|
|
331
|
-
mins = (elapsed / 60).to_i
|
|
332
|
-
line += " (#{mins}m)"
|
|
333
|
-
end
|
|
334
|
-
line += " [#{meta['tmux_session']}:#{meta['tmux_window']}]" if meta['tmux_session']
|
|
335
|
-
end
|
|
336
|
-
preview = q.task_preview(t[:name], t[:status].to_sym)
|
|
337
|
-
line += " #{preview}" if preview
|
|
338
|
-
puts line
|
|
362
|
+
list_cmd = proc { |*args|
|
|
363
|
+
opts = Hiiro::Options.parse(args) do
|
|
364
|
+
flag(:all, short: :a, desc: 'Show all tasks without limit; use pager if output exceeds terminal height')
|
|
339
365
|
end
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
h.add_subcmd(:list) {
|
|
343
|
-
tasks = q.all_tasks
|
|
344
|
-
if tasks.empty?
|
|
366
|
+
lines = q.list_lines(all: opts.all)
|
|
367
|
+
if lines.empty?
|
|
345
368
|
puts "No tasks"
|
|
346
369
|
next
|
|
347
370
|
end
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
elapsed = Time.now - Time.parse(started)
|
|
355
|
-
mins = (elapsed / 60).to_i
|
|
356
|
-
line += " (#{mins}m)"
|
|
357
|
-
end
|
|
358
|
-
line += " [#{meta['tmux_session']}:#{meta['tmux_window']}]" if meta['tmux_session']
|
|
371
|
+
if opts.all
|
|
372
|
+
terminal_lines = ENV['LINES']&.to_i || 24
|
|
373
|
+
if lines.size > terminal_lines
|
|
374
|
+
IO.popen(ENV['PAGER'] || 'less', 'w') { |io| io.puts lines }
|
|
375
|
+
else
|
|
376
|
+
puts lines
|
|
359
377
|
end
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
puts line
|
|
378
|
+
else
|
|
379
|
+
puts lines
|
|
363
380
|
end
|
|
364
381
|
}
|
|
365
382
|
|
|
383
|
+
h.add_subcmd(:ls, &list_cmd)
|
|
384
|
+
h.add_subcmd(:list, &list_cmd)
|
|
385
|
+
|
|
366
386
|
h.add_subcmd(:status) {
|
|
367
387
|
tasks = q.all_tasks
|
|
368
388
|
if tasks.empty?
|
data/lib/hiiro/version.rb
CHANGED