hiiro 0.1.81 → 0.1.83
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-branch +109 -76
- data/bin/h-pane +0 -11
- data/bin/h-pr +115 -72
- data/bin/h-session +0 -12
- data/bin/h-window +0 -12
- data/lib/hiiro/version.rb +1 -1
- data/lib/hiiro.rb +0 -9
- metadata +6 -12
- data/bin/h-home +0 -62
- data/bin/h-html +0 -380
- data/bin/h-mic +0 -93
- data/bin/h-note +0 -119
- data/bin/h-remind +0 -614
- data/bin/h-serve +0 -6
- data/bin/h-video +0 -523
- data/lib/hiiro/history/entry.rb +0 -136
- data/lib/hiiro/history.rb +0 -454
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 599e565871c2afc37ae659954e4ccf01568bed4375032fb9d8ba21007e7e2673
|
|
4
|
+
data.tar.gz: 4fdcbf43a79f2b63ec3f95ecd003973cb3175b4ef0b6cdbb0cbc36444439b259
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8945bc066927371fa804f6b0deef00ee36f84be0fc9178033e52d5228ce5f6b895b114cb3a0a37129e491f2c8d4a1156cfce36e18740d28048fa7885ad1f792
|
|
7
|
+
data.tar.gz: 31acaf4e204e3be5f8c6fddbf6009f0e757bad86e3dcbe5c770637c05d848a6c4a65c42552617e015bd8b55171aecc9d2fde86b58e6b16ed06ef23f8bdef6179
|
data/bin/h-branch
CHANGED
|
@@ -19,10 +19,6 @@ class BranchManager
|
|
|
19
19
|
puts
|
|
20
20
|
puts "Subcommands:"
|
|
21
21
|
puts " save Record current branch for this task"
|
|
22
|
-
puts " history List branch history (oldest to newest)"
|
|
23
|
-
puts " history --worktree=X Filter by worktree"
|
|
24
|
-
puts " history --task=X Filter by task"
|
|
25
|
-
puts " history --session=X Filter by tmux session"
|
|
26
22
|
puts " current Show current branch info"
|
|
27
23
|
end
|
|
28
24
|
|
|
@@ -62,35 +58,6 @@ class BranchManager
|
|
|
62
58
|
true
|
|
63
59
|
end
|
|
64
60
|
|
|
65
|
-
def history(args = [])
|
|
66
|
-
filters = parse_filters(args)
|
|
67
|
-
data = load_data
|
|
68
|
-
branches = data['branches'] || []
|
|
69
|
-
|
|
70
|
-
if branches.empty?
|
|
71
|
-
puts "No branches recorded."
|
|
72
|
-
puts "Use 'h branch save' to record the current branch."
|
|
73
|
-
return
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Apply filters
|
|
77
|
-
branches = filter_entries(branches, filters)
|
|
78
|
-
|
|
79
|
-
if branches.empty?
|
|
80
|
-
puts "No branches match the given filters."
|
|
81
|
-
return
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Sort by created_at (oldest first)
|
|
85
|
-
branches = branches.sort_by { |b| b['created_at'] || '' }
|
|
86
|
-
|
|
87
|
-
puts "Branch history (oldest to newest):"
|
|
88
|
-
puts
|
|
89
|
-
branches.each_with_index do |branch, idx|
|
|
90
|
-
puts format_entry(branch, idx + 1)
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
61
|
def current
|
|
95
62
|
branch_name = current_branch
|
|
96
63
|
unless branch_name
|
|
@@ -132,48 +99,6 @@ class BranchManager
|
|
|
132
99
|
}
|
|
133
100
|
end
|
|
134
101
|
|
|
135
|
-
def parse_filters(args)
|
|
136
|
-
filters = {}
|
|
137
|
-
args.each do |arg|
|
|
138
|
-
case arg
|
|
139
|
-
when /^--worktree=(.+)$/
|
|
140
|
-
filters[:worktree] = $1
|
|
141
|
-
when /^--task=(.+)$/
|
|
142
|
-
filters[:task] = $1
|
|
143
|
-
when /^--session=(.+)$/
|
|
144
|
-
filters[:session] = $1
|
|
145
|
-
when /^-w(.+)$/
|
|
146
|
-
filters[:worktree] = $1
|
|
147
|
-
when /^-t(.+)$/
|
|
148
|
-
filters[:task] = $1
|
|
149
|
-
when /^-s(.+)$/
|
|
150
|
-
filters[:session] = $1
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
filters
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def filter_entries(entries, filters)
|
|
157
|
-
entries.select do |entry|
|
|
158
|
-
next false if filters[:worktree] && !entry['worktree']&.include?(filters[:worktree])
|
|
159
|
-
next false if filters[:task] && !entry['task']&.include?(filters[:task])
|
|
160
|
-
next false if filters[:session] && entry.dig('tmux', 'session') != filters[:session]
|
|
161
|
-
true
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def format_entry(entry, num)
|
|
166
|
-
lines = []
|
|
167
|
-
lines << "#{num}. #{entry['name']}"
|
|
168
|
-
lines << " Task: #{entry['task'] || '(none)'}"
|
|
169
|
-
lines << " Worktree: #{entry['worktree'] || '(none)'}"
|
|
170
|
-
if entry['tmux']
|
|
171
|
-
lines << " Tmux: #{entry['tmux']['session']}/#{entry['tmux']['window']}"
|
|
172
|
-
end
|
|
173
|
-
lines << " Created: #{entry['created_at']}"
|
|
174
|
-
lines.join("\n")
|
|
175
|
-
end
|
|
176
|
-
|
|
177
102
|
def show_entry(entry)
|
|
178
103
|
puts " Branch: #{entry[:name]}"
|
|
179
104
|
puts " Task: #{entry[:task] || '(none)'}"
|
|
@@ -204,7 +129,6 @@ manager = BranchManager.new(hiiro)
|
|
|
204
129
|
|
|
205
130
|
hiiro.add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
|
|
206
131
|
hiiro.add_subcmd(:save) { manager.save }
|
|
207
|
-
hiiro.add_subcmd(:history) { |*args| manager.history(args) }
|
|
208
132
|
hiiro.add_subcmd(:current) { manager.current }
|
|
209
133
|
hiiro.add_subcmd(:select) do |*args|
|
|
210
134
|
branches = hiiro.git.branches(sort_by: 'authordate', ignore_case: true)
|
|
@@ -375,6 +299,115 @@ hiiro.add_subcmd(:behind) { |*args|
|
|
|
375
299
|
puts "#{branch} is #{count} commit(s) behind #{behind_of}"
|
|
376
300
|
}
|
|
377
301
|
|
|
302
|
+
hiiro.add_subcmd(:log) { |upstream = nil|
|
|
303
|
+
upstream ||= %w[origin/master master origin/main main].find { |ref|
|
|
304
|
+
system("git rev-parse --verify #{ref} >/dev/null 2>&1")
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
unless upstream
|
|
308
|
+
puts "Cannot find master or origin/master"
|
|
309
|
+
next
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
forkpoint = `git merge-base --fork-point #{upstream} HEAD 2>/dev/null`.strip
|
|
313
|
+
if forkpoint.empty?
|
|
314
|
+
forkpoint = `git merge-base #{upstream} HEAD 2>/dev/null`.strip
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
if forkpoint.empty?
|
|
318
|
+
puts "Could not find fork point"
|
|
319
|
+
next
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
system('git', 'log', '--oneline', '--decorate', "#{forkpoint}..HEAD")
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
hiiro.add_subcmd(:forkpoint) { |upstream = nil, branch = nil|
|
|
326
|
+
branch ||= 'HEAD'
|
|
327
|
+
upstream ||= %w[origin/master master origin/main main].find { |ref|
|
|
328
|
+
system("git rev-parse --verify #{ref} >/dev/null 2>&1")
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
unless upstream
|
|
332
|
+
puts "Cannot find master or origin/master"
|
|
333
|
+
next
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
forkpoint = `git merge-base --fork-point #{upstream} #{branch} 2>/dev/null`.strip
|
|
337
|
+
|
|
338
|
+
if forkpoint.empty?
|
|
339
|
+
# Fallback to regular merge-base if fork-point fails
|
|
340
|
+
forkpoint = `git merge-base #{upstream} #{branch} 2>/dev/null`.strip
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if forkpoint.empty?
|
|
344
|
+
puts "Could not find fork point between #{upstream} and #{branch}"
|
|
345
|
+
else
|
|
346
|
+
puts forkpoint
|
|
347
|
+
end
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
hiiro.add_subcmd(:changed) { |*args|
|
|
351
|
+
show_all = args.delete('-a') || args.delete('--all')
|
|
352
|
+
upstream = args.first
|
|
353
|
+
|
|
354
|
+
upstream ||= %w[origin/master master origin/main main].find { |ref|
|
|
355
|
+
system("git rev-parse --verify #{ref} >/dev/null 2>&1")
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
unless upstream
|
|
359
|
+
puts "Cannot find master or origin/master"
|
|
360
|
+
next
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
forkpoint = `git merge-base --fork-point #{upstream} HEAD 2>/dev/null`.strip
|
|
364
|
+
if forkpoint.empty?
|
|
365
|
+
forkpoint = `git merge-base #{upstream} HEAD 2>/dev/null`.strip
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
if forkpoint.empty?
|
|
369
|
+
puts "Could not find fork point"
|
|
370
|
+
next
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
if show_all
|
|
374
|
+
system('git', 'diff', '--name-only', "#{forkpoint}...HEAD")
|
|
375
|
+
else
|
|
376
|
+
system('git', 'diff', '--name-only', '--relative', "#{forkpoint}...HEAD")
|
|
377
|
+
end
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
hiiro.add_subcmd(:changed) { |*args|
|
|
382
|
+
show_all = args.delete('-a') || args.delete('--all')
|
|
383
|
+
upstream = args.first
|
|
384
|
+
|
|
385
|
+
upstream ||= %w[origin/master master origin/main main].find { |ref|
|
|
386
|
+
system("git rev-parse --verify #{ref} >/dev/null 2>&1")
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
unless upstream
|
|
390
|
+
puts "Cannot find master or origin/master"
|
|
391
|
+
next
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
forkpoint = `git merge-base --fork-point #{upstream} HEAD 2>/dev/null`.strip
|
|
395
|
+
if forkpoint.empty?
|
|
396
|
+
forkpoint = `git merge-base #{upstream} HEAD 2>/dev/null`.strip
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
if forkpoint.empty?
|
|
400
|
+
puts "Could not find fork point"
|
|
401
|
+
next
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
if show_all
|
|
405
|
+
system('git', 'diff', '--name-only', "#{forkpoint}...HEAD")
|
|
406
|
+
else
|
|
407
|
+
system('git', 'diff', '--name-only', '--relative', "#{forkpoint}...HEAD")
|
|
408
|
+
end
|
|
409
|
+
}
|
|
410
|
+
|
|
378
411
|
hiiro.add_subcmd(:ancestor) { |*args|
|
|
379
412
|
case args.length
|
|
380
413
|
when 0
|
data/bin/h-pane
CHANGED
|
@@ -60,17 +60,6 @@ o.add_subcmd(:resize) { |*args|
|
|
|
60
60
|
system('tmux', 'resize-pane', *args)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
o.add_subcmd(:history) { |pane_id=nil|
|
|
64
|
-
entries = Hiiro::History.by_pane(pane_id)
|
|
65
|
-
if entries.empty?
|
|
66
|
-
puts "No history for this pane"
|
|
67
|
-
else
|
|
68
|
-
puts "History for pane: #{pane_id || ENV['TMUX_PANE'] || 'current'}"
|
|
69
|
-
puts
|
|
70
|
-
entries.last(20).each_with_index { |e, i| puts e.oneline(i + 1) }
|
|
71
|
-
end
|
|
72
|
-
}
|
|
73
|
-
|
|
74
63
|
o.add_subcmd(:select) do |*args|
|
|
75
64
|
# Get all panes with details
|
|
76
65
|
output = `tmux list-panes -a -F '\#{session_name}:\#{window_index}.\#{pane_index} [\#{pane_current_command}] \#{pane_current_path}'`.strip
|
data/bin/h-pr
CHANGED
|
@@ -21,10 +21,6 @@ class PRManager
|
|
|
21
21
|
puts
|
|
22
22
|
puts "Subcommands:"
|
|
23
23
|
puts " save [PR_NUMBER] Record PR for this task (auto-detects if omitted)"
|
|
24
|
-
puts " history List PR history (oldest to newest)"
|
|
25
|
-
puts " history --worktree=X Filter by worktree"
|
|
26
|
-
puts " history --task=X Filter by task"
|
|
27
|
-
puts " history --session=X Filter by tmux session"
|
|
28
24
|
puts " current Show current branch's PR info"
|
|
29
25
|
puts " open [PR_NUMBER] Open PR in browser"
|
|
30
26
|
puts " view [PR_NUMBER] View PR details in terminal"
|
|
@@ -67,35 +63,6 @@ class PRManager
|
|
|
67
63
|
true
|
|
68
64
|
end
|
|
69
65
|
|
|
70
|
-
def history(args = [])
|
|
71
|
-
filters = parse_filters(args)
|
|
72
|
-
data = load_data
|
|
73
|
-
prs = data['prs'] || []
|
|
74
|
-
|
|
75
|
-
if prs.empty?
|
|
76
|
-
puts "No PRs recorded."
|
|
77
|
-
puts "Use 'h pr save' to record the current PR."
|
|
78
|
-
return
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Apply filters
|
|
82
|
-
prs = filter_entries(prs, filters)
|
|
83
|
-
|
|
84
|
-
if prs.empty?
|
|
85
|
-
puts "No PRs match the given filters."
|
|
86
|
-
return
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Sort by created_at (oldest first)
|
|
90
|
-
prs = prs.sort_by { |p| p['created_at'] || '' }
|
|
91
|
-
|
|
92
|
-
puts "PR history (oldest to newest):"
|
|
93
|
-
puts
|
|
94
|
-
prs.each_with_index do |pr, idx|
|
|
95
|
-
puts format_entry(pr, idx + 1)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
66
|
def current
|
|
100
67
|
pr_info = fetch_pr_info
|
|
101
68
|
unless pr_info
|
|
@@ -169,36 +136,6 @@ class PRManager
|
|
|
169
136
|
}
|
|
170
137
|
end
|
|
171
138
|
|
|
172
|
-
def parse_filters(args)
|
|
173
|
-
filters = {}
|
|
174
|
-
args.each do |arg|
|
|
175
|
-
case arg
|
|
176
|
-
when /^--worktree=(.+)$/
|
|
177
|
-
filters[:worktree] = $1
|
|
178
|
-
when /^--task=(.+)$/
|
|
179
|
-
filters[:task] = $1
|
|
180
|
-
when /^--session=(.+)$/
|
|
181
|
-
filters[:session] = $1
|
|
182
|
-
when /^-w(.+)$/
|
|
183
|
-
filters[:worktree] = $1
|
|
184
|
-
when /^-t(.+)$/
|
|
185
|
-
filters[:task] = $1
|
|
186
|
-
when /^-s(.+)$/
|
|
187
|
-
filters[:session] = $1
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
filters
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def filter_entries(entries, filters)
|
|
194
|
-
entries.select do |entry|
|
|
195
|
-
next false if filters[:worktree] && !entry['worktree']&.include?(filters[:worktree])
|
|
196
|
-
next false if filters[:task] && !entry['task']&.include?(filters[:task])
|
|
197
|
-
next false if filters[:session] && entry.dig('tmux', 'session') != filters[:session]
|
|
198
|
-
true
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
139
|
def format_entry(entry, num)
|
|
203
140
|
lines = []
|
|
204
141
|
lines << "#{num}. PR ##{entry['number']}: #{entry['title']}"
|
|
@@ -295,7 +232,7 @@ class PinnedPRManager
|
|
|
295
232
|
end
|
|
296
233
|
|
|
297
234
|
def fetch_pr_info(pr_number)
|
|
298
|
-
fields = 'number,title,url,headRefName,state,statusCheckRollup'
|
|
235
|
+
fields = 'number,title,url,headRefName,state,statusCheckRollup,reviewDecision,reviews,isDraft,mergeable'
|
|
299
236
|
output = `gh pr view #{pr_number} --json #{fields} 2>/dev/null`.strip
|
|
300
237
|
return nil if output.empty?
|
|
301
238
|
JSON.parse(output)
|
|
@@ -325,6 +262,10 @@ class PinnedPRManager
|
|
|
325
262
|
pr['state'] = info['state']
|
|
326
263
|
pr['title'] = info['title']
|
|
327
264
|
pr['checks'] = summarize_checks(info['statusCheckRollup'])
|
|
265
|
+
pr['reviews'] = summarize_reviews(info['reviews'])
|
|
266
|
+
pr['review_decision'] = info['reviewDecision']
|
|
267
|
+
pr['is_draft'] = info['isDraft']
|
|
268
|
+
pr['mergeable'] = info['mergeable']
|
|
328
269
|
pr['last_checked'] = Time.now.iso8601
|
|
329
270
|
pr
|
|
330
271
|
end
|
|
@@ -343,28 +284,124 @@ class PinnedPRManager
|
|
|
343
284
|
{ 'total' => total, 'success' => success, 'pending' => pending, 'failed' => failed }
|
|
344
285
|
end
|
|
345
286
|
|
|
287
|
+
def summarize_reviews(reviews)
|
|
288
|
+
return nil unless reviews.is_a?(Array) && !reviews.empty?
|
|
289
|
+
|
|
290
|
+
# Get the latest review state per author
|
|
291
|
+
latest_by_author = {}
|
|
292
|
+
reviews.each do |review|
|
|
293
|
+
author = review['author']['login'] rescue nil
|
|
294
|
+
next unless author
|
|
295
|
+
# Only track if it's a meaningful state
|
|
296
|
+
state = review['state']
|
|
297
|
+
next unless %w[APPROVED CHANGES_REQUESTED COMMENTED].include?(state)
|
|
298
|
+
latest_by_author[author] = state
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
approved = latest_by_author.values.count { |s| s == 'APPROVED' }
|
|
302
|
+
changes_requested = latest_by_author.values.count { |s| s == 'CHANGES_REQUESTED' }
|
|
303
|
+
commented = latest_by_author.values.count { |s| s == 'COMMENTED' }
|
|
304
|
+
|
|
305
|
+
{ 'approved' => approved, 'changes_requested' => changes_requested, 'commented' => commented, 'reviewers' => latest_by_author }
|
|
306
|
+
end
|
|
307
|
+
|
|
346
308
|
def display_pinned(pr, idx = nil)
|
|
347
309
|
num = idx ? "#{(idx + 1).to_s.rjust(3)}." : ""
|
|
348
310
|
state_icon = case pr['state']
|
|
349
311
|
when 'MERGED' then '[M]'
|
|
350
312
|
when 'CLOSED' then '[X]'
|
|
351
|
-
else '[O]'
|
|
313
|
+
else pr['is_draft'] ? '[D]' : '[O]'
|
|
352
314
|
end
|
|
353
315
|
|
|
354
316
|
checks_str = if pr['checks']
|
|
355
317
|
c = pr['checks']
|
|
356
318
|
if c['failed'] > 0
|
|
357
|
-
"
|
|
319
|
+
" checks:#{c['success']}/#{c['total']} FAIL:#{c['failed']}"
|
|
358
320
|
elsif c['pending'] > 0
|
|
359
|
-
"
|
|
321
|
+
" checks:#{c['success']}/#{c['total']} pending:#{c['pending']}"
|
|
360
322
|
else
|
|
361
|
-
"
|
|
323
|
+
" checks:#{c['success']}/#{c['total']}"
|
|
362
324
|
end
|
|
363
325
|
else
|
|
364
326
|
""
|
|
365
327
|
end
|
|
366
328
|
|
|
367
|
-
|
|
329
|
+
reviews_str = if pr['reviews']
|
|
330
|
+
r = pr['reviews']
|
|
331
|
+
parts = []
|
|
332
|
+
parts << "#{r['approved']} approved" if r['approved'] > 0
|
|
333
|
+
parts << "#{r['changes_requested']} changes" if r['changes_requested'] > 0
|
|
334
|
+
parts.empty? ? "" : " | #{parts.join(', ')}"
|
|
335
|
+
else
|
|
336
|
+
""
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
"#{num} #{state_icon} ##{pr['number']} #{pr['title']}#{checks_str}#{reviews_str}".strip
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def display_detailed(pr, idx = nil)
|
|
343
|
+
lines = []
|
|
344
|
+
num = idx ? "#{idx + 1}." : ""
|
|
345
|
+
|
|
346
|
+
state_str = case pr['state']
|
|
347
|
+
when 'MERGED' then 'MERGED'
|
|
348
|
+
when 'CLOSED' then 'CLOSED'
|
|
349
|
+
else pr['is_draft'] ? 'DRAFT' : 'OPEN'
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
lines << "#{num} ##{pr['number']} - #{pr['title']}"
|
|
353
|
+
lines << " State: #{state_str}"
|
|
354
|
+
lines << " Branch: #{pr['headRefName']}" if pr['headRefName']
|
|
355
|
+
lines << " URL: #{pr['url']}" if pr['url']
|
|
356
|
+
|
|
357
|
+
# Checks
|
|
358
|
+
if pr['checks']
|
|
359
|
+
c = pr['checks']
|
|
360
|
+
check_status = if c['failed'] > 0
|
|
361
|
+
"FAILING (#{c['success']}/#{c['total']} passed, #{c['failed']} failed)"
|
|
362
|
+
elsif c['pending'] > 0
|
|
363
|
+
"PENDING (#{c['success']}/#{c['total']} passed, #{c['pending']} pending)"
|
|
364
|
+
else
|
|
365
|
+
"PASSING (#{c['success']}/#{c['total']})"
|
|
366
|
+
end
|
|
367
|
+
lines << " Checks: #{check_status}"
|
|
368
|
+
else
|
|
369
|
+
lines << " Checks: (none)"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Reviews
|
|
373
|
+
if pr['reviews']
|
|
374
|
+
r = pr['reviews']
|
|
375
|
+
review_parts = []
|
|
376
|
+
review_parts << "#{r['approved']} approved" if r['approved'] > 0
|
|
377
|
+
review_parts << "#{r['changes_requested']} requesting changes" if r['changes_requested'] > 0
|
|
378
|
+
review_parts << "#{r['commented']} commented" if r['commented'] > 0
|
|
379
|
+
|
|
380
|
+
if review_parts.any?
|
|
381
|
+
lines << " Reviews: #{review_parts.join(', ')}"
|
|
382
|
+
if r['reviewers'] && !r['reviewers'].empty?
|
|
383
|
+
r['reviewers'].each do |author, state|
|
|
384
|
+
icon = case state
|
|
385
|
+
when 'APPROVED' then '+'
|
|
386
|
+
when 'CHANGES_REQUESTED' then '-'
|
|
387
|
+
else '?'
|
|
388
|
+
end
|
|
389
|
+
lines << " #{icon} #{author}: #{state.downcase.gsub('_', ' ')}"
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
else
|
|
393
|
+
lines << " Reviews: (none)"
|
|
394
|
+
end
|
|
395
|
+
else
|
|
396
|
+
lines << " Reviews: (not fetched)"
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Mergeable
|
|
400
|
+
if pr['mergeable']
|
|
401
|
+
lines << " Mergeable: #{pr['mergeable']}"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
lines.join("\n")
|
|
368
405
|
end
|
|
369
406
|
end
|
|
370
407
|
|
|
@@ -441,7 +478,6 @@ hiiro.add_subcmd(:link) { |*args|
|
|
|
441
478
|
|
|
442
479
|
hiiro.add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
|
|
443
480
|
hiiro.add_subcmd(:save) { |pr_number=nil| manager.save(pr_number) }
|
|
444
|
-
hiiro.add_subcmd(:history) { |*args| manager.history(args) }
|
|
445
481
|
hiiro.add_subcmd(:current) { manager.current }
|
|
446
482
|
hiiro.add_subcmd(:open) { |pr_number=nil| manager.open(pr_number) }
|
|
447
483
|
hiiro.add_subcmd(:view) { |pr_number=nil| manager.view(pr_number) }
|
|
@@ -586,16 +622,23 @@ hiiro.add_subcmd(:status) do |*args|
|
|
|
586
622
|
next
|
|
587
623
|
end
|
|
588
624
|
|
|
625
|
+
compact = args.include?('-c') || args.include?('--compact')
|
|
626
|
+
|
|
589
627
|
puts "Refreshing status for #{pinned.length} pinned PR(s)..."
|
|
590
628
|
puts
|
|
591
629
|
|
|
592
630
|
pinned.each_with_index do |pr, idx|
|
|
593
631
|
pinned_manager.refresh_status(pr)
|
|
594
|
-
|
|
632
|
+
if compact
|
|
633
|
+
puts pinned_manager.display_pinned(pr, idx)
|
|
634
|
+
else
|
|
635
|
+
puts pinned_manager.display_detailed(pr, idx)
|
|
636
|
+
puts
|
|
637
|
+
end
|
|
595
638
|
end
|
|
596
639
|
|
|
597
640
|
pinned_manager.save_pinned(pinned)
|
|
598
|
-
puts
|
|
641
|
+
puts "---"
|
|
599
642
|
puts "Status updated at #{Time.now.strftime('%H:%M:%S')}"
|
|
600
643
|
end
|
|
601
644
|
|
data/bin/h-session
CHANGED
|
@@ -60,16 +60,4 @@ o.add_subcmd(:info) { |*args|
|
|
|
60
60
|
system('tmux', 'display-message', '-p', '#{session_name}: #{session_windows} windows, #{session_attached} attached', *args)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
o.add_subcmd(:history) { |session_name=nil|
|
|
64
|
-
session_name ||= `tmux display-message -p '#S'`.strip if ENV['TMUX']
|
|
65
|
-
entries = Hiiro::History.by_session(session_name)
|
|
66
|
-
if entries.empty?
|
|
67
|
-
puts "No history for this session"
|
|
68
|
-
else
|
|
69
|
-
puts "History for session: #{session_name || 'current'}"
|
|
70
|
-
puts
|
|
71
|
-
entries.last(20).each_with_index { |e, i| puts e.oneline(i + 1) }
|
|
72
|
-
end
|
|
73
|
-
}
|
|
74
|
-
|
|
75
63
|
o.run
|
data/bin/h-window
CHANGED
|
@@ -56,18 +56,6 @@ o.add_subcmd(:unlink) { |*args|
|
|
|
56
56
|
system('tmux', 'unlink-window', *args)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
o.add_subcmd(:history) { |window_id=nil|
|
|
60
|
-
window_id ||= `tmux display-message -p '#I'`.strip if ENV['TMUX']
|
|
61
|
-
entries = Hiiro::History.by_window(window_id)
|
|
62
|
-
if entries.empty?
|
|
63
|
-
puts "No history for this window"
|
|
64
|
-
else
|
|
65
|
-
puts "History for window: #{window_id || 'current'}"
|
|
66
|
-
puts
|
|
67
|
-
entries.last(20).each_with_index { |e, i| puts e.oneline(i + 1) }
|
|
68
|
-
end
|
|
69
|
-
}
|
|
70
|
-
|
|
71
59
|
o.add_subcmd(:select) do |*args|
|
|
72
60
|
# Get all windows with details
|
|
73
61
|
output = `tmux list-windows -a -F '\#{session_name}:\#{window_index} \#{window_name} [\#{window_panes} panes]'`.strip
|
data/lib/hiiro/version.rb
CHANGED
data/lib/hiiro.rb
CHANGED
|
@@ -6,7 +6,6 @@ require "pry"
|
|
|
6
6
|
require_relative "hiiro/version"
|
|
7
7
|
require_relative "hiiro/prefix_matcher"
|
|
8
8
|
require_relative "hiiro/git"
|
|
9
|
-
require_relative "hiiro/history"
|
|
10
9
|
require_relative "hiiro/options"
|
|
11
10
|
require_relative "hiiro/notification"
|
|
12
11
|
require_relative "hiiro/sk"
|
|
@@ -39,7 +38,6 @@ class Hiiro
|
|
|
39
38
|
bin_name = values[:bin_name] || $0
|
|
40
39
|
|
|
41
40
|
new(bin_name, *args, logging: logging, **values).tap do |hiiro|
|
|
42
|
-
History.load(hiiro)
|
|
43
41
|
hiiro.load_plugins(*plugins)
|
|
44
42
|
|
|
45
43
|
hiiro.add_subcommand(:pry) { |*args|
|
|
@@ -104,16 +102,9 @@ class Hiiro
|
|
|
104
102
|
@todo_manager ||= TodoManager.new
|
|
105
103
|
end
|
|
106
104
|
|
|
107
|
-
def history
|
|
108
|
-
@history ||= History.new
|
|
109
|
-
end
|
|
110
|
-
|
|
111
105
|
def run
|
|
112
106
|
result = runner.run(*args)
|
|
113
107
|
|
|
114
|
-
# Track command after running (only saves if state changed and in task context)
|
|
115
|
-
History.track(cmd: full_command, hiiro: self)
|
|
116
|
-
|
|
117
108
|
handle_result(result)
|
|
118
109
|
|
|
119
110
|
exit 1
|
metadata
CHANGED
|
@@ -1,13 +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.83
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Toyota
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-02-14 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: pry
|
|
@@ -72,21 +73,14 @@ files:
|
|
|
72
73
|
- bin/h-buffer
|
|
73
74
|
- bin/h-commit
|
|
74
75
|
- bin/h-config
|
|
75
|
-
- bin/h-home
|
|
76
|
-
- bin/h-html
|
|
77
76
|
- bin/h-link
|
|
78
|
-
- bin/h-mic
|
|
79
|
-
- bin/h-note
|
|
80
77
|
- bin/h-pane
|
|
81
78
|
- bin/h-plugin
|
|
82
79
|
- bin/h-pr
|
|
83
80
|
- bin/h-project
|
|
84
|
-
- bin/h-remind
|
|
85
|
-
- bin/h-serve
|
|
86
81
|
- bin/h-session
|
|
87
82
|
- bin/h-sha
|
|
88
83
|
- bin/h-todo
|
|
89
|
-
- bin/h-video
|
|
90
84
|
- bin/h-window
|
|
91
85
|
- bin/h-wtree
|
|
92
86
|
- docs/README.md
|
|
@@ -107,8 +101,6 @@ files:
|
|
|
107
101
|
- lib/hiiro/git/remote.rb
|
|
108
102
|
- lib/hiiro/git/worktree.rb
|
|
109
103
|
- lib/hiiro/git/worktrees.rb
|
|
110
|
-
- lib/hiiro/history.rb
|
|
111
|
-
- lib/hiiro/history/entry.rb
|
|
112
104
|
- lib/hiiro/notification.rb
|
|
113
105
|
- lib/hiiro/options.rb
|
|
114
106
|
- lib/hiiro/prefix_matcher.rb
|
|
@@ -134,6 +126,7 @@ metadata:
|
|
|
134
126
|
homepage_uri: https://github.com/unixsuperhero/hiiro
|
|
135
127
|
source_code_uri: https://github.com/unixsuperhero/hiiro
|
|
136
128
|
changelog_uri: https://github.com/unixsuperhero/hiiro/blob/main/CHANGELOG.md
|
|
129
|
+
post_install_message:
|
|
137
130
|
rdoc_options: []
|
|
138
131
|
require_paths:
|
|
139
132
|
- lib
|
|
@@ -148,7 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
148
141
|
- !ruby/object:Gem::Version
|
|
149
142
|
version: '0'
|
|
150
143
|
requirements: []
|
|
151
|
-
rubygems_version: 3.
|
|
144
|
+
rubygems_version: 3.3.7
|
|
145
|
+
signing_key:
|
|
152
146
|
specification_version: 4
|
|
153
147
|
summary: A lightweight CLI framework for Ruby
|
|
154
148
|
test_files: []
|