hiiro 0.1.211 → 0.1.212
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 +164 -331
- data/lib/hiiro/queue.rb +5 -5
- data/lib/hiiro/tasks.rb +13 -16
- 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: 595b4d37c446a020b8c28e0698d57f918844638f564a13fbb5cc1eaf63ac8ae1
|
|
4
|
+
data.tar.gz: 85dee985c6307a9e9d818d4bec6be82b8f2b6dea4192ad3ac948548070ffecf0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9cdd2c615aa42d82551f50fc00c97166f90f6c6918e888daf0570897eb036d6a251d20c96f46160744dae06d01035daca31ed65532a516249bdb5af4a263d1e2
|
|
7
|
+
data.tar.gz: c74ad66f0945e8689e6f0d414365d4760c16d6115d67566fece2848f683d88b328bb614101b2b5a704d2a1e591e160c890f82b84cd30e10c7ef7bda2b5d98d54
|
data/bin/h-pr
CHANGED
|
@@ -9,168 +9,6 @@ require "tempfile"
|
|
|
9
9
|
|
|
10
10
|
Hiiro.load_env
|
|
11
11
|
|
|
12
|
-
class PRManager
|
|
13
|
-
attr_reader :hiiro
|
|
14
|
-
|
|
15
|
-
def initialize(hiiro)
|
|
16
|
-
@hiiro = hiiro
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def save(pr_number = nil)
|
|
20
|
-
pr_info = fetch_pr_info(pr_number)
|
|
21
|
-
unless pr_info
|
|
22
|
-
puts "ERROR: Could not find PR"
|
|
23
|
-
puts "Make sure you have the gh CLI installed and authenticated."
|
|
24
|
-
return false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
entry = build_entry(pr_info)
|
|
28
|
-
unless entry[:task]
|
|
29
|
-
puts "WARNING: Not in a task session, saving without task info"
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
data = load_data
|
|
33
|
-
data['prs'] ||= []
|
|
34
|
-
|
|
35
|
-
# Check if this PR is already recorded for this task
|
|
36
|
-
existing = data['prs'].find do |p|
|
|
37
|
-
p['number'] == pr_info['number'] && p['task'] == entry[:task]
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
if existing
|
|
41
|
-
# Update existing entry
|
|
42
|
-
existing.merge!(entry.transform_keys(&:to_s))
|
|
43
|
-
existing['updated_at'] = Time.now.iso8601
|
|
44
|
-
puts "Updated PR ##{pr_info['number']} for task '#{entry[:task]}'"
|
|
45
|
-
else
|
|
46
|
-
# Add new entry
|
|
47
|
-
data['prs'] << entry.transform_keys(&:to_s).merge('created_at' => Time.now.iso8601)
|
|
48
|
-
puts "Saved PR ##{pr_info['number']} for task '#{entry[:task]}'"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
save_data(data)
|
|
52
|
-
show_entry(entry)
|
|
53
|
-
true
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def current
|
|
57
|
-
pr_info = fetch_pr_info
|
|
58
|
-
unless pr_info
|
|
59
|
-
puts "No PR found for current branch."
|
|
60
|
-
puts "Create one with 'gh pr create' or specify a PR number."
|
|
61
|
-
return false
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
entry = build_entry(pr_info)
|
|
65
|
-
puts "Current PR info:"
|
|
66
|
-
puts
|
|
67
|
-
show_entry(entry)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def open(pr_number = nil)
|
|
71
|
-
if pr_number
|
|
72
|
-
system('gh', 'pr', 'view', pr_number.to_s, '--web')
|
|
73
|
-
else
|
|
74
|
-
system('gh', 'pr', 'view', '--web')
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def view(pr_number = nil)
|
|
79
|
-
if pr_number
|
|
80
|
-
system('gh', 'pr', 'view', pr_number.to_s)
|
|
81
|
-
else
|
|
82
|
-
system('gh', 'pr', 'view')
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
def fetch_pr_info(pr_number = nil)
|
|
89
|
-
cmd = if pr_number
|
|
90
|
-
['gh', 'pr', 'view', pr_number.to_s, '--json', 'number,title,url,headRefName,state']
|
|
91
|
-
else
|
|
92
|
-
['gh', 'pr', 'view', '--json', 'number,title,url,headRefName,state']
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
output = `#{cmd.join(' ')} 2>/dev/null`
|
|
96
|
-
return nil if output.empty?
|
|
97
|
-
|
|
98
|
-
JSON.parse(output)
|
|
99
|
-
rescue JSON::ParserError
|
|
100
|
-
nil
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def build_entry(pr_info)
|
|
104
|
-
current_task = Environment.current.task
|
|
105
|
-
tmux_info = capture_tmux_info
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
number: pr_info['number'],
|
|
109
|
-
title: pr_info['title'],
|
|
110
|
-
url: pr_info['url'],
|
|
111
|
-
branch: pr_info['headRefName'],
|
|
112
|
-
state: pr_info['state'],
|
|
113
|
-
worktree: current_task&.tree_name,
|
|
114
|
-
task: current_task&.name,
|
|
115
|
-
tmux: tmux_info
|
|
116
|
-
}
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def capture_tmux_info
|
|
120
|
-
return nil unless ENV['TMUX']
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
'session' => `tmux display-message -p '#S'`.strip,
|
|
124
|
-
'window' => `tmux display-message -p '#W'`.strip,
|
|
125
|
-
'pane' => ENV['TMUX_PANE']
|
|
126
|
-
}
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def format_entry(entry, num)
|
|
130
|
-
lines = []
|
|
131
|
-
lines << "#{num}. PR ##{entry['number']}: #{entry['title']}"
|
|
132
|
-
lines << " Branch: #{entry['branch']}"
|
|
133
|
-
lines << " State: #{entry['state']}"
|
|
134
|
-
lines << " Task: #{entry['task'] || '(none)'}"
|
|
135
|
-
lines << " Worktree: #{entry['worktree'] || '(none)'}"
|
|
136
|
-
if entry['tmux']
|
|
137
|
-
lines << " Tmux: #{entry['tmux']['session']}/#{entry['tmux']['window']}"
|
|
138
|
-
end
|
|
139
|
-
lines << " Created: #{entry['created_at']}"
|
|
140
|
-
lines << " URL: #{entry['url']}"
|
|
141
|
-
lines.join("\n")
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def show_entry(entry)
|
|
145
|
-
puts " PR: ##{entry[:number]}"
|
|
146
|
-
puts " Title: #{entry[:title]}"
|
|
147
|
-
puts " Branch: #{entry[:branch]}"
|
|
148
|
-
puts " State: #{entry[:state]}"
|
|
149
|
-
puts " URL: #{entry[:url]}"
|
|
150
|
-
puts " Task: #{entry[:task] || '(none)'}"
|
|
151
|
-
puts " Worktree: #{entry[:worktree] || '(none)'}"
|
|
152
|
-
if entry[:tmux]
|
|
153
|
-
puts " Tmux session: #{entry[:tmux]['session']}"
|
|
154
|
-
puts " Tmux window: #{entry[:tmux]['window']}"
|
|
155
|
-
puts " Tmux pane: #{entry[:tmux]['pane']}"
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def data_file
|
|
160
|
-
File.join(Dir.home, '.config', 'hiiro', 'prs.yml')
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def load_data
|
|
164
|
-
return {} unless File.exist?(data_file)
|
|
165
|
-
YAML.safe_load_file(data_file) || {}
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def save_data(data)
|
|
169
|
-
FileUtils.mkdir_p(File.dirname(data_file))
|
|
170
|
-
File.write(data_file, YAML.dump(data))
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
12
|
class PinnedPRManager
|
|
175
13
|
PINNED_FILE = File.join(Dir.home, '.config/hiiro/pinned_prs.yml')
|
|
176
14
|
|
|
@@ -618,14 +456,13 @@ class PinnedPRManager
|
|
|
618
456
|
end
|
|
619
457
|
|
|
620
458
|
Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
621
|
-
manager = PRManager.new(self)
|
|
622
459
|
pinned_manager = PinnedPRManager.new
|
|
623
460
|
|
|
624
461
|
resolve_pr = ->(ref) {
|
|
625
462
|
if ref.nil?
|
|
626
463
|
pinned = pinned_manager.load_pinned
|
|
627
464
|
if pinned.empty?
|
|
628
|
-
STDERR.puts "No
|
|
465
|
+
STDERR.puts "No tracked PRs. Use a PR number or 'h pr track' to track PRs."
|
|
629
466
|
return nil
|
|
630
467
|
end
|
|
631
468
|
lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
|
|
@@ -724,15 +561,13 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
724
561
|
}
|
|
725
562
|
|
|
726
563
|
add_subcmd(:edit) { edit_files(__FILE__) }
|
|
727
|
-
add_subcmd(:save) { |pr_number=nil| manager.save(pr_number) }
|
|
728
|
-
add_subcmd(:current) { manager.current }
|
|
729
564
|
add_subcmd(:open) { |pr_number=nil|
|
|
730
565
|
pr_number = resolve_pr.call(pr_number)
|
|
731
|
-
|
|
566
|
+
system('gh', 'pr', 'view', pr_number.to_s, '--web') if pr_number
|
|
732
567
|
}
|
|
733
568
|
add_subcmd(:view) { |pr_number=nil|
|
|
734
569
|
pr_number = resolve_pr.call(pr_number)
|
|
735
|
-
|
|
570
|
+
system('gh', 'pr', 'view', pr_number.to_s) if pr_number
|
|
736
571
|
}
|
|
737
572
|
|
|
738
573
|
add_subcmd(:select) do |*args|
|
|
@@ -789,107 +624,68 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
789
624
|
end
|
|
790
625
|
end
|
|
791
626
|
|
|
792
|
-
# === PR
|
|
627
|
+
# === PR Tracking ===
|
|
793
628
|
|
|
794
|
-
add_subcmd(:
|
|
629
|
+
add_subcmd(:track) do |ref = nil|
|
|
795
630
|
pr_info = nil
|
|
796
631
|
|
|
797
632
|
case ref
|
|
798
|
-
when
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
end
|
|
806
|
-
else
|
|
807
|
-
# Select from my PRs
|
|
808
|
-
my_prs = pinned_manager.fetch_my_prs
|
|
809
|
-
if my_prs.empty?
|
|
810
|
-
puts "No open PRs found"
|
|
811
|
-
next
|
|
812
|
-
end
|
|
633
|
+
when nil
|
|
634
|
+
# Select from my open PRs
|
|
635
|
+
my_prs = pinned_manager.fetch_my_prs
|
|
636
|
+
if my_prs.empty?
|
|
637
|
+
puts "No open PRs found"
|
|
638
|
+
next
|
|
639
|
+
end
|
|
813
640
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
641
|
+
lines = my_prs.each_with_object({}) do |pr, h|
|
|
642
|
+
display = "##{pr['number']} [#{pr['headRefName']}] #{pr['title']}"
|
|
643
|
+
h[display] = pr
|
|
644
|
+
end
|
|
818
645
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
646
|
+
pr_info = fuzzyfind_from_map(lines)
|
|
647
|
+
unless pr_info
|
|
648
|
+
puts "No PR selected"
|
|
649
|
+
next
|
|
650
|
+
end
|
|
651
|
+
when '-'
|
|
652
|
+
# Track current branch's PR
|
|
653
|
+
pr_info = pinned_manager.fetch_current_branch_pr
|
|
654
|
+
unless pr_info
|
|
655
|
+
puts "No PR found for current branch"
|
|
656
|
+
next
|
|
824
657
|
end
|
|
825
658
|
when /^\d+$/
|
|
826
|
-
#
|
|
659
|
+
# Track by PR number
|
|
827
660
|
pr_info = pinned_manager.fetch_pr_info(ref)
|
|
828
661
|
unless pr_info
|
|
829
662
|
puts "PR ##{ref} not found"
|
|
830
663
|
next
|
|
831
664
|
end
|
|
832
665
|
else
|
|
833
|
-
puts "Usage: h pr
|
|
834
|
-
puts " -
|
|
835
|
-
puts " <num>
|
|
666
|
+
puts "Usage: h pr track [-|PR_NUMBER]"
|
|
667
|
+
puts " - Track current branch's PR"
|
|
668
|
+
puts " <num> Track PR by number"
|
|
836
669
|
puts " (none) Select from your open PRs"
|
|
837
670
|
next
|
|
838
671
|
end
|
|
839
672
|
|
|
840
|
-
pinned_manager.
|
|
841
|
-
|
|
842
|
-
end
|
|
843
|
-
|
|
844
|
-
add_subcmd(:unpin) do |ref = nil, *unpin_args|
|
|
845
|
-
pinned = pinned_manager.load_pinned
|
|
846
|
-
|
|
847
|
-
if pinned.empty?
|
|
848
|
-
puts "No pinned PRs"
|
|
849
|
-
next
|
|
850
|
-
end
|
|
851
|
-
|
|
852
|
-
pr_number = nil
|
|
853
|
-
|
|
854
|
-
if ref.nil?
|
|
855
|
-
# Select from pinned PRs
|
|
856
|
-
lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
|
|
857
|
-
h[pinned_manager.display_pinned(pr, idx)] = pr['number'].to_s
|
|
858
|
-
end
|
|
859
|
-
|
|
860
|
-
pr_number = fuzzyfind_from_map(lines)
|
|
861
|
-
unless pr_number
|
|
862
|
-
puts "No PR selected"
|
|
863
|
-
next
|
|
864
|
-
end
|
|
865
|
-
elsif ref =~ /^\d+$/
|
|
866
|
-
pr_number = ref
|
|
867
|
-
else
|
|
868
|
-
puts "Usage: h pr unpin [PR_NUMBER]"
|
|
673
|
+
if pinned_manager.pinned?(pr_info['number'])
|
|
674
|
+
puts "Already tracking ##{pr_info['number']}"
|
|
869
675
|
next
|
|
870
676
|
end
|
|
871
677
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
678
|
+
context = {}
|
|
679
|
+
env = Environment.current rescue nil
|
|
680
|
+
if env
|
|
681
|
+
task = env.task
|
|
682
|
+
context['task'] = task.name if task
|
|
683
|
+
context['worktree'] = task.tree_name if task&.tree_name
|
|
684
|
+
context['tmux_session'] = env.session&.name
|
|
876
685
|
end
|
|
877
|
-
end
|
|
878
686
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
if pinned.empty?
|
|
883
|
-
puts "No pinned PRs"
|
|
884
|
-
next
|
|
885
|
-
end
|
|
886
|
-
|
|
887
|
-
puts "Pinned PRs:"
|
|
888
|
-
puts
|
|
889
|
-
|
|
890
|
-
pinned.each_with_index do |pr, idx|
|
|
891
|
-
puts pinned_manager.display_pinned(pr, idx)
|
|
892
|
-
end
|
|
687
|
+
pinned_manager.pin(pr_info.merge(context))
|
|
688
|
+
puts "Now tracking ##{pr_info['number']}: #{pr_info['title']}"
|
|
893
689
|
end
|
|
894
690
|
|
|
895
691
|
add_subcmd(:ls) do |*ls_args|
|
|
@@ -898,7 +694,7 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
898
694
|
pinned = pinned_manager.load_pinned
|
|
899
695
|
|
|
900
696
|
if pinned.empty?
|
|
901
|
-
puts "No
|
|
697
|
+
puts "No tracked PRs"
|
|
902
698
|
next
|
|
903
699
|
end
|
|
904
700
|
|
|
@@ -922,19 +718,13 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
922
718
|
pinned = pinned_manager.load_pinned
|
|
923
719
|
|
|
924
720
|
if pinned.empty?
|
|
925
|
-
puts "No
|
|
721
|
+
puts "No tracked PRs"
|
|
926
722
|
next
|
|
927
723
|
end
|
|
928
724
|
|
|
929
|
-
compact = status_args.include?('-c') || status_args.include?('--compact')
|
|
930
|
-
|
|
931
725
|
pinned.each_with_index do |pr, idx|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
else
|
|
935
|
-
puts pinned_manager.display_detailed(pr, idx)
|
|
936
|
-
puts
|
|
937
|
-
end
|
|
726
|
+
puts pinned_manager.display_detailed(pr, idx)
|
|
727
|
+
puts
|
|
938
728
|
end
|
|
939
729
|
|
|
940
730
|
last_checked = pinned.filter_map { |pr| pr['last_checked'] }.max
|
|
@@ -946,7 +736,7 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
946
736
|
pinned = pinned_manager.load_pinned
|
|
947
737
|
|
|
948
738
|
if pinned.empty?
|
|
949
|
-
puts "No
|
|
739
|
+
puts "No tracked PRs to update"
|
|
950
740
|
next
|
|
951
741
|
end
|
|
952
742
|
|
|
@@ -1014,11 +804,10 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1014
804
|
end
|
|
1015
805
|
|
|
1016
806
|
add_subcmd(:prune) do |*args|
|
|
1017
|
-
# Alias to auto-unpin
|
|
1018
807
|
pinned = pinned_manager.load_pinned
|
|
1019
808
|
|
|
1020
809
|
if pinned.empty?
|
|
1021
|
-
puts "No
|
|
810
|
+
puts "No tracked PRs"
|
|
1022
811
|
next
|
|
1023
812
|
end
|
|
1024
813
|
|
|
@@ -1212,25 +1001,7 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1212
1001
|
system('tmux', 'switch-client', '-t', target_session)
|
|
1213
1002
|
end
|
|
1214
1003
|
|
|
1215
|
-
add_subcmd(:
|
|
1216
|
-
pr_info = pinned_manager.fetch_current_branch_pr
|
|
1217
|
-
unless pr_info
|
|
1218
|
-
puts "No PR for current branch"
|
|
1219
|
-
next
|
|
1220
|
-
end
|
|
1221
|
-
|
|
1222
|
-
if pinned_manager.pinned?(pr_info['number'])
|
|
1223
|
-
puts "Already tracking ##{pr_info['number']}"
|
|
1224
|
-
next
|
|
1225
|
-
end
|
|
1226
|
-
|
|
1227
|
-
full_info = pinned_manager.fetch_pr_info(pr_info['number'])
|
|
1228
|
-
pinned_manager.pin(full_info)
|
|
1229
|
-
puts "Now tracking ##{pr_info['number']}: #{pr_info['title']}"
|
|
1230
|
-
end
|
|
1231
|
-
|
|
1232
|
-
add_subcmd(:ignore) do |ref = nil|
|
|
1233
|
-
# Alias to unpin
|
|
1004
|
+
add_subcmd(:rm) do |ref = nil|
|
|
1234
1005
|
pinned = pinned_manager.load_pinned
|
|
1235
1006
|
|
|
1236
1007
|
if pinned.empty?
|
|
@@ -1253,103 +1024,165 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
|
|
|
1253
1024
|
elsif ref =~ /^\d+$/
|
|
1254
1025
|
pr_number = ref
|
|
1255
1026
|
else
|
|
1256
|
-
puts "Usage: h pr
|
|
1027
|
+
puts "Usage: h pr rm [PR_NUMBER]"
|
|
1257
1028
|
next
|
|
1258
1029
|
end
|
|
1259
1030
|
|
|
1260
1031
|
if pinned_manager.unpin(pr_number)
|
|
1261
|
-
puts "
|
|
1032
|
+
puts "Removed PR ##{pr_number} from tracking"
|
|
1262
1033
|
else
|
|
1263
1034
|
puts "PR ##{pr_number} was not tracked"
|
|
1264
1035
|
end
|
|
1265
1036
|
end
|
|
1266
1037
|
|
|
1267
|
-
|
|
1268
|
-
# Alias to ignore/unpin
|
|
1269
|
-
pinned = pinned_manager.load_pinned
|
|
1038
|
+
# === PR State ===
|
|
1270
1039
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1040
|
+
add_subcmd(:ready) do |ref = nil|
|
|
1041
|
+
pr_number = resolve_pr.call(ref)
|
|
1042
|
+
next unless pr_number
|
|
1043
|
+
system('gh', 'pr', 'ready', pr_number.to_s)
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
add_subcmd(:'to-draft') do |ref = nil|
|
|
1047
|
+
pr_number = resolve_pr.call(ref)
|
|
1048
|
+
next unless pr_number
|
|
1049
|
+
system('gh', 'pr', 'ready', '--draft', pr_number.to_s)
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
# === PR Actions ===
|
|
1053
|
+
|
|
1054
|
+
add_subcmd(:diff) do |ref = nil|
|
|
1055
|
+
pr_number = resolve_pr.call(ref)
|
|
1056
|
+
next unless pr_number
|
|
1057
|
+
system('gh', 'pr', 'diff', pr_number.to_s)
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
add_subcmd(:checkout) do |ref = nil|
|
|
1061
|
+
pr_number = resolve_pr.call(ref)
|
|
1062
|
+
next unless pr_number
|
|
1063
|
+
system('gh', 'pr', 'checkout', pr_number.to_s)
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
add_subcmd(:merge) do |ref = nil, *merge_args|
|
|
1067
|
+
pr_number = resolve_pr.call(ref)
|
|
1068
|
+
next unless pr_number
|
|
1069
|
+
system('gh', 'pr', 'merge', pr_number.to_s, *merge_args)
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
add_subcmd(:comment) do |ref = nil|
|
|
1073
|
+
pr_number = resolve_pr.call(ref)
|
|
1074
|
+
next unless pr_number
|
|
1075
|
+
|
|
1076
|
+
tmpfile = Tempfile.new(['pr-comment-', '.md'])
|
|
1077
|
+
tmpfile.close
|
|
1078
|
+
edit_files(tmpfile.path)
|
|
1079
|
+
|
|
1080
|
+
body = File.read(tmpfile.path).strip
|
|
1081
|
+
tmpfile.unlink
|
|
1082
|
+
|
|
1083
|
+
if body.empty?
|
|
1084
|
+
puts "Aborted: empty comment"
|
|
1273
1085
|
next
|
|
1274
1086
|
end
|
|
1275
1087
|
|
|
1276
|
-
pr_number
|
|
1088
|
+
system('gh', 'pr', 'comment', pr_number.to_s, '--body', body)
|
|
1089
|
+
end
|
|
1277
1090
|
|
|
1278
|
-
|
|
1279
|
-
lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
|
|
1280
|
-
h[pinned_manager.display_pinned(pr, idx)] = pr['number'].to_s
|
|
1281
|
-
end
|
|
1091
|
+
# === Comment Templates ===
|
|
1282
1092
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
puts "Usage: h pr rm [PR_NUMBER]"
|
|
1093
|
+
TEMPLATES_DIR = File.join(Dir.home, '.config', 'hiiro', 'pr_templates')
|
|
1094
|
+
|
|
1095
|
+
add_subcmd(:templates) do
|
|
1096
|
+
FileUtils.mkdir_p(TEMPLATES_DIR)
|
|
1097
|
+
files = Dir.glob(File.join(TEMPLATES_DIR, '*.md')).sort
|
|
1098
|
+
|
|
1099
|
+
if files.empty?
|
|
1100
|
+
puts "No templates found. Use 'h pr new-template <name>' to create one."
|
|
1292
1101
|
next
|
|
1293
1102
|
end
|
|
1294
1103
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1104
|
+
puts "Comment templates:"
|
|
1105
|
+
puts
|
|
1106
|
+
files.each do |f|
|
|
1107
|
+
name = File.basename(f, '.md')
|
|
1108
|
+
first_line = File.readlines(f).first&.strip || ''
|
|
1109
|
+
puts " #{name.ljust(20)} #{first_line}"
|
|
1299
1110
|
end
|
|
1300
1111
|
end
|
|
1301
1112
|
|
|
1302
|
-
add_subcmd(:'
|
|
1303
|
-
|
|
1113
|
+
add_subcmd(:'new-template') do |name = nil|
|
|
1114
|
+
FileUtils.mkdir_p(TEMPLATES_DIR)
|
|
1304
1115
|
|
|
1305
|
-
|
|
1306
|
-
puts "
|
|
1116
|
+
unless name
|
|
1117
|
+
STDERR.puts "Usage: h pr new-template <name>"
|
|
1307
1118
|
next
|
|
1308
1119
|
end
|
|
1309
1120
|
|
|
1310
|
-
|
|
1311
|
-
closed = pinned.select { |pr| pr['state'] == 'CLOSED' }
|
|
1121
|
+
path = File.join(TEMPLATES_DIR, "#{name}.md")
|
|
1312
1122
|
|
|
1313
|
-
if
|
|
1314
|
-
puts "
|
|
1315
|
-
next
|
|
1123
|
+
if File.exist?(path)
|
|
1124
|
+
puts "Template '#{name}' already exists, opening for edit..."
|
|
1316
1125
|
end
|
|
1317
1126
|
|
|
1318
|
-
|
|
1319
|
-
puts
|
|
1127
|
+
edit_files(path)
|
|
1128
|
+
puts "Template saved: #{path}"
|
|
1129
|
+
end
|
|
1320
1130
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1131
|
+
add_subcmd(:'from-template') do |ref = nil|
|
|
1132
|
+
FileUtils.mkdir_p(TEMPLATES_DIR)
|
|
1133
|
+
files = Dir.glob(File.join(TEMPLATES_DIR, '*.md')).sort
|
|
1134
|
+
|
|
1135
|
+
if files.empty?
|
|
1136
|
+
STDERR.puts "No templates found. Use 'h pr new-template <name>' to create one."
|
|
1137
|
+
next
|
|
1323
1138
|
end
|
|
1324
1139
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1140
|
+
template_map = files.each_with_object({}) do |f, h|
|
|
1141
|
+
name = File.basename(f, '.md')
|
|
1142
|
+
first_line = File.readlines(f).first&.strip || ''
|
|
1143
|
+
h["#{name.ljust(20)} #{first_line}"] = f
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
template_path = fuzzyfind_from_map(template_map)
|
|
1147
|
+
next unless template_path
|
|
1148
|
+
|
|
1149
|
+
pr_number = resolve_pr.call(ref)
|
|
1150
|
+
next unless pr_number
|
|
1151
|
+
|
|
1152
|
+
body = File.read(template_path).strip
|
|
1153
|
+
system('gh', 'pr', 'comment', pr_number.to_s, '--body', body)
|
|
1327
1154
|
end
|
|
1328
1155
|
|
|
1329
|
-
|
|
1330
|
-
pinned = pinned_manager.load_pinned
|
|
1156
|
+
# === PR List by Context ===
|
|
1331
1157
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1158
|
+
add_subcmd(:'for-task') do |task_name = nil|
|
|
1159
|
+
tracked = pinned_manager.load_pinned
|
|
1160
|
+
|
|
1161
|
+
if tracked.empty?
|
|
1162
|
+
puts "No tracked PRs"
|
|
1334
1163
|
next
|
|
1335
1164
|
end
|
|
1336
1165
|
|
|
1337
|
-
|
|
1166
|
+
task_name ||= begin
|
|
1167
|
+
env = Environment.current rescue nil
|
|
1168
|
+
env&.task&.name
|
|
1169
|
+
end
|
|
1338
1170
|
|
|
1339
|
-
|
|
1340
|
-
puts "
|
|
1171
|
+
unless task_name
|
|
1172
|
+
STDERR.puts "Not in a task context. Pass a task name: h pr for-task <name>"
|
|
1341
1173
|
next
|
|
1342
1174
|
end
|
|
1343
1175
|
|
|
1344
|
-
|
|
1345
|
-
puts
|
|
1176
|
+
matches = tracked.select { |pr| pr['task'] == task_name }
|
|
1346
1177
|
|
|
1347
|
-
|
|
1348
|
-
puts "
|
|
1349
|
-
|
|
1178
|
+
if matches.empty?
|
|
1179
|
+
puts "No tracked PRs for task '#{task_name}'"
|
|
1180
|
+
next
|
|
1350
1181
|
end
|
|
1351
1182
|
|
|
1183
|
+
puts "PRs for task '#{task_name}':"
|
|
1352
1184
|
puts
|
|
1353
|
-
|
|
1185
|
+
matches.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
|
|
1354
1186
|
end
|
|
1187
|
+
|
|
1355
1188
|
end
|
data/lib/hiiro/queue.rb
CHANGED
|
@@ -238,7 +238,7 @@ class Hiiro
|
|
|
238
238
|
|
|
239
239
|
mapping = tasks.each_with_object({}) do |task, h|
|
|
240
240
|
line = format("%-25s tree: %-20s", task.name, task.tree_name || '(none)')
|
|
241
|
-
h[line] = task
|
|
241
|
+
h[line] = task
|
|
242
242
|
end
|
|
243
243
|
|
|
244
244
|
hiiro.fuzzyfind_from_map(mapping)
|
|
@@ -246,7 +246,7 @@ class Hiiro
|
|
|
246
246
|
|
|
247
247
|
def resolve_task_info(opts, hiiro, default_task_info)
|
|
248
248
|
task_name = if opts.choose
|
|
249
|
-
select_task(hiiro)
|
|
249
|
+
select_task(hiiro)&.name
|
|
250
250
|
elsif opts.task
|
|
251
251
|
opts.task
|
|
252
252
|
else
|
|
@@ -322,7 +322,7 @@ class Hiiro
|
|
|
322
322
|
parent_hiiro.make_child do |h|
|
|
323
323
|
h.add_subcmd(:watch) {
|
|
324
324
|
q.queue_dirs
|
|
325
|
-
current_version =
|
|
325
|
+
current_version = `gem which hiiro`.sub(/.*hiiro-/, '').sub(/\/.*/, '')
|
|
326
326
|
puts current_version:;
|
|
327
327
|
puts "Watching #{File.join(DIR, 'pending')} ..."
|
|
328
328
|
puts "Press Ctrl-C to stop"
|
|
@@ -330,8 +330,8 @@ class Hiiro
|
|
|
330
330
|
loop do
|
|
331
331
|
loops += 1
|
|
332
332
|
if current_version
|
|
333
|
-
latest =
|
|
334
|
-
if loops %
|
|
333
|
+
latest = `gem which hiiro`.sub(/.*hiiro-/, '').sub(/\/.*/, '') rescue nil
|
|
334
|
+
if loops % 20 == 0
|
|
335
335
|
puts current_version:, latest:;
|
|
336
336
|
end
|
|
337
337
|
|
data/lib/hiiro/tasks.rb
CHANGED
|
@@ -401,10 +401,9 @@ class Hiiro
|
|
|
401
401
|
branch_col = all_data.map { |_, d| d[:branch].length }.max || 0
|
|
402
402
|
|
|
403
403
|
all_data.each do |task, d|
|
|
404
|
-
display_name = scope == :subtask ? task.short_name : task.name
|
|
405
404
|
line = format("%-#{name_col}s %-#{tree_col}s %-#{branch_col}s %s",
|
|
406
405
|
d[:name], d[:tree], d[:branch], d[:session])
|
|
407
|
-
mapping[line] =
|
|
406
|
+
mapping[line] = task
|
|
408
407
|
end
|
|
409
408
|
|
|
410
409
|
# Add non-task tmux sessions (exclude sessions that belong to tasks)
|
|
@@ -413,7 +412,7 @@ class Hiiro
|
|
|
413
412
|
extra_sessions = environment.all_sessions.reject { |s| task_session_names.include?(s.name) }
|
|
414
413
|
extra_sessions.sort_by(&:name).each do |session|
|
|
415
414
|
line = format("%-25s (tmux session)", session.name)
|
|
416
|
-
mapping[line] =
|
|
415
|
+
mapping[line] = session
|
|
417
416
|
end
|
|
418
417
|
end
|
|
419
418
|
|
|
@@ -623,16 +622,14 @@ class Hiiro
|
|
|
623
622
|
selected = tm.select_task_interactive
|
|
624
623
|
next unless selected
|
|
625
624
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
else
|
|
635
|
-
task_name = selected
|
|
625
|
+
case selected
|
|
626
|
+
when Hiiro::Tmux::Session
|
|
627
|
+
h.start_tmux_session(selected.name)
|
|
628
|
+
puts "Switched to session '#{selected.name}'"
|
|
629
|
+
next
|
|
630
|
+
when Hiiro::Task
|
|
631
|
+
tm.switch_to_task(selected, app_name: app_name)
|
|
632
|
+
next
|
|
636
633
|
end
|
|
637
634
|
end
|
|
638
635
|
|
|
@@ -713,9 +710,9 @@ class Hiiro
|
|
|
713
710
|
|
|
714
711
|
h.add_subcmd(:stop) do |task_name=nil|
|
|
715
712
|
if task_name.nil?
|
|
716
|
-
|
|
717
|
-
next unless
|
|
718
|
-
task_name =
|
|
713
|
+
selected = tm.select_task_interactive
|
|
714
|
+
next unless selected
|
|
715
|
+
task_name = selected.name
|
|
719
716
|
end
|
|
720
717
|
task = tm.task_by_name(task_name)
|
|
721
718
|
tm.stop_task(task)
|
data/lib/hiiro/version.rb
CHANGED