hiiro 0.1.159 → 0.1.161

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 394980810fd90dc9fdd82383a671502fd78669690479d6b3e6ba0fb4ed6510fc
4
- data.tar.gz: 189dffc8b1e175751572114424b7ce0078e6eae386aa3c3993ae0d0689c37733
3
+ metadata.gz: bba280c058e3179fcf5430d57c5ad67725dfdd5787d05e256f6cefd5561b809d
4
+ data.tar.gz: 67315c92d268a4163193049a36b5d9e3e29fd70378efc9560a1d748d45216ed7
5
5
  SHA512:
6
- metadata.gz: 6cef595bf8648318ade523cd214a43d76960398543168803fe9a2cb2948dbe4d065e259a5de3aa5ebc60a2e703fa3de21e049b5c9c6295a3c3602c8a5f6524fd
7
- data.tar.gz: '009cf0ef87a09777f322bba3868bd06197d1b8207f94e580fe7e63ed1437f2a5c965c35b66b7479f3fb819a0cf90dd64d9327f38fc76926053371c7a6b984b7f'
6
+ metadata.gz: 652249cc95ecae99c1b4de0514117ee5c00a1911e00c85c4c236a15f9593493dadb92cd0e4c6311cd8d930f4fe40ca51fa790e0b03154a678795b3ada931c814
7
+ data.tar.gz: 030563f1521442ab306ff67e8bf454c7729ce711f35a02371d3b69da3d1c36e578cf3ad99b10b3f6dd3164c359cc6977511393a1e69a2a81d321d376e23a9fff
data/bin/h-cpr ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "hiiro"
4
+ require "json"
5
+
6
+ Hiiro.load_env
7
+
8
+ output = `gh pr view --json number 2>/dev/null`.strip
9
+ if output.empty?
10
+ STDERR.puts "No PR found for current branch"
11
+ exit 1
12
+ end
13
+
14
+ pr_number = begin
15
+ JSON.parse(output)['number'].to_s
16
+ rescue JSON::ParserError
17
+ STDERR.puts "Could not parse PR info"
18
+ exit 1
19
+ end
20
+
21
+ if ARGV.empty?
22
+ exec('h', 'pr', 'view', pr_number)
23
+ end
24
+
25
+ subcmd, *rest = ARGV
26
+ exec('h', 'pr', subcmd, pr_number, *rest)
data/bin/h-pr CHANGED
@@ -268,6 +268,87 @@ class PinnedPRManager
268
268
  end
269
269
 
270
270
 
271
+ def batch_fetch_pr_info(pr_numbers)
272
+ return {} if pr_numbers.empty?
273
+
274
+ # Build GraphQL query to fetch multiple PRs at once
275
+ pr_queries = pr_numbers.map.with_index do |num, idx|
276
+ <<~GRAPHQL.strip
277
+ pr#{idx}: pullRequest(number: #{num}) {
278
+ number
279
+ title
280
+ url
281
+ headRefName
282
+ state
283
+ isDraft
284
+ mergeable
285
+ reviewDecision
286
+ statusCheckRollup {
287
+ contexts(last: 100) {
288
+ nodes {
289
+ ... on CheckRun {
290
+ conclusion
291
+ status
292
+ }
293
+ ... on StatusContext {
294
+ state
295
+ }
296
+ }
297
+ }
298
+ }
299
+ reviews(last: 50) {
300
+ nodes {
301
+ author {
302
+ login
303
+ }
304
+ state
305
+ }
306
+ }
307
+ }
308
+ GRAPHQL
309
+ end
310
+
311
+ query = <<~GRAPHQL
312
+ query {
313
+ repository(owner: "instacart", name: "carrot") {
314
+ #{pr_queries.join("\n")}
315
+ }
316
+ }
317
+ GRAPHQL
318
+
319
+ result = `gh api graphql -f query='#{query.gsub("'", "'\\''")}' 2>/dev/null`
320
+ return {} if result.empty?
321
+
322
+ data = JSON.parse(result)
323
+ repo_data = data.dig('data', 'repository')
324
+ return {} unless repo_data
325
+
326
+ # Convert GraphQL response to hash keyed by PR number
327
+ pr_info_by_number = {}
328
+ pr_numbers.each_with_index do |num, idx|
329
+ pr_data = repo_data["pr#{idx}"]
330
+ next unless pr_data
331
+
332
+ # Transform GraphQL response to match gh pr view format
333
+ pr_info_by_number[num] = {
334
+ 'number' => pr_data['number'],
335
+ 'title' => pr_data['title'],
336
+ 'url' => pr_data['url'],
337
+ 'headRefName' => pr_data['headRefName'],
338
+ 'state' => pr_data['state'],
339
+ 'isDraft' => pr_data['isDraft'],
340
+ 'mergeable' => pr_data['mergeable'],
341
+ 'reviewDecision' => pr_data['reviewDecision'],
342
+ 'statusCheckRollup' => pr_data.dig('statusCheckRollup', 'contexts', 'nodes'),
343
+ 'reviews' => pr_data.dig('reviews', 'nodes') || []
344
+ }
345
+ end
346
+
347
+ pr_info_by_number
348
+ rescue JSON::ParserError, StandardError
349
+ {}
350
+ end
351
+
271
352
  def refresh_all_status(prs, force: false)
272
353
  prs_to_refresh = prs.select { |pr| needs_refresh?(pr, force: force) }
273
354
 
@@ -276,7 +357,23 @@ class PinnedPRManager
276
357
  return prs
277
358
  end
278
359
 
279
- prs_to_refresh.each { |pr| refresh_status(pr, force: true) }
360
+ pr_numbers = prs_to_refresh.map { |pr| pr['number'] }
361
+ infos = batch_fetch_pr_info(pr_numbers)
362
+
363
+ prs_to_refresh.each do |pr|
364
+ info = infos[pr['number']]
365
+ next unless info
366
+
367
+ pr['state'] = info['state']
368
+ pr['title'] = info['title']
369
+ pr['checks'] = summarize_checks(info['statusCheckRollup'])
370
+ pr['reviews'] = summarize_reviews(info['reviews'])
371
+ pr['review_decision'] = info['reviewDecision']
372
+ pr['is_draft'] = info['isDraft']
373
+ pr['mergeable'] = info['mergeable']
374
+ pr['last_checked'] = Time.now.iso8601
375
+ end
376
+
280
377
  prs
281
378
  end
282
379
 
@@ -353,15 +450,18 @@ class PinnedPRManager
353
450
  ""
354
451
  end
355
452
 
356
- reviews_str = if pr['reviews']
357
- r = pr['reviews']
358
- parts = []
359
- parts << "#{r['approved']} approved" if r['approved'] > 0
360
- parts << "#{r['changes_requested']} changes" if r['changes_requested'] > 0
361
- parts.empty? ? "" : " | #{parts.join(', ')}"
362
- else
363
- ""
364
- end
453
+ reviews_str =
454
+ if pr['reviews']
455
+ reviews = pr['reviews'].map do |r|
456
+ parts = []
457
+ parts << "#{r['approved']} approved" if r['approved'] > 0
458
+ parts << "#{r['changes_requested']} changes" if r['changes_requested'] > 0
459
+ parts.empty? ? "" : " | #{parts.join(', ')}"
460
+ end
461
+ reviews.join('; ')
462
+ else
463
+ ""
464
+ end
365
465
 
366
466
  "#{num} #{state_icon} ##{pr['number']} #{pr['title']}#{checks_str}#{reviews_str}".strip
367
467
  end
@@ -436,34 +536,55 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
436
536
  manager = PRManager.new(self)
437
537
  pinned_manager = PinnedPRManager.new
438
538
 
539
+ resolve_pr = ->(ref) {
540
+ if ref.nil?
541
+ pinned = pinned_manager.load_pinned
542
+ if pinned.empty?
543
+ STDERR.puts "No pinned PRs. Use a PR number or 'h pr pin' to track PRs."
544
+ return nil
545
+ end
546
+ lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
547
+ h[pinned_manager.display_pinned(pr, idx)] = pr['number'].to_s
548
+ end
549
+ return fuzzyfind_from_map(lines)
550
+ end
551
+
552
+ if (pin_value = pins.get(ref.to_s))
553
+ return pin_value
554
+ end
555
+
556
+ pinned = pinned_manager.load_pinned
557
+
558
+ if pinned.any? { |pr| pr['number'].to_s == ref.to_s }
559
+ return ref.to_s
560
+ end
561
+
562
+ if ref.to_s =~ /^\d+$/ && ref.to_i > 0
563
+ idx = ref.to_i - 1
564
+ return pinned[idx]['number'].to_s if idx < pinned.length
565
+ end
566
+
567
+ ref.to_s
568
+ }
569
+
439
570
  watch_block = ->(original_pr_number=nil, *watch_args) {
440
571
  watch = get_value(:watch)
441
572
  fail_fast = get_value(:fail_fast)
442
573
 
443
- pr_valid = false
444
- pr_number = nil
445
- if !original_pr_number.nil? && pin_value = pins.get(original_pr_number)
446
- pr_valid = true
447
- pr_number = pin_value
448
- end
449
-
450
- pr_args = [pr_valid ? pr_number : original_pr_number].compact
451
- check_args = [*pr_args, *watch_args]
574
+ pr_number = resolve_pr.call(original_pr_number)
575
+ return unless pr_number
452
576
 
453
577
  base_cmd = %w[gh pr checks]
454
578
  base_cmd << '--watch' if watch
455
579
  base_cmd << '--fail-fast' if fail_fast
456
580
 
457
- command = [
458
- *base_cmd,
459
- *check_args,
460
- ]
581
+ command = [*base_cmd, pr_number, *watch_args]
461
582
 
462
- puts command: command, base_cmd: base_cmd, args: check_args;
583
+ puts command: command, base_cmd: base_cmd, args: [pr_number, *watch_args];
463
584
 
464
585
  result = system(*command)
465
586
 
466
- pr_info = JSON.parse(`gh pr view #{pr_args.first} --json url,number,title`)
587
+ pr_info = JSON.parse(`gh pr view #{pr_number} --json url,number,title`)
467
588
  pr_url = pr_info['url']
468
589
  pr_num = pr_info['number']
469
590
  pr_title = pr_info['title']
@@ -508,8 +629,14 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
508
629
  add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
509
630
  add_subcmd(:save) { |pr_number=nil| manager.save(pr_number) }
510
631
  add_subcmd(:current) { manager.current }
511
- add_subcmd(:open) { |pr_number=nil| manager.open(pr_number) }
512
- add_subcmd(:view) { |pr_number=nil| manager.view(pr_number) }
632
+ add_subcmd(:open) { |pr_number=nil|
633
+ pr_number = resolve_pr.call(pr_number)
634
+ manager.open(pr_number) if pr_number
635
+ }
636
+ add_subcmd(:view) { |pr_number=nil|
637
+ pr_number = resolve_pr.call(pr_number)
638
+ manager.view(pr_number) if pr_number
639
+ }
513
640
 
514
641
  add_subcmd(:select) do |*args|
515
642
  # Get my open PRs
@@ -745,7 +745,7 @@ class Hiiro
745
745
  end
746
746
 
747
747
  def write_shell_script(path, cmds)
748
- lines = ["#!/usr/bin/env bash"]
748
+ lines = ["#!/bin/bash"]
749
749
  lines.concat(cmds)
750
750
  File.write(path, lines.join("\n") + "\n")
751
751
  File.chmod(0755, path)
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.159"
2
+ VERSION = "0.1.161"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiiro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.159
4
+ version: 0.1.161
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-03-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: pry
@@ -90,6 +89,7 @@ files:
90
89
  - bin/h-claude
91
90
  - bin/h-commit
92
91
  - bin/h-config
92
+ - bin/h-cpr
93
93
  - bin/h-jumplist
94
94
  - bin/h-link
95
95
  - bin/h-misc
@@ -156,12 +156,12 @@ files:
156
156
  - plugins/pins.rb
157
157
  - plugins/project.rb
158
158
  - record-demo.sh
159
- - scripts/compare
160
- - scripts/install
161
- - scripts/publish
162
- - scripts/sync
163
- - scripts/test
164
- - scripts/update
159
+ - script/compare
160
+ - script/install
161
+ - script/publish
162
+ - script/sync
163
+ - script/test
164
+ - script/update
165
165
  - slides.md
166
166
  homepage: https://github.com/unixsuperhero/hiiro
167
167
  licenses:
@@ -170,7 +170,6 @@ metadata:
170
170
  homepage_uri: https://github.com/unixsuperhero/hiiro
171
171
  source_code_uri: https://github.com/unixsuperhero/hiiro
172
172
  changelog_uri: https://github.com/unixsuperhero/hiiro/blob/main/CHANGELOG.md
173
- post_install_message:
174
173
  rdoc_options: []
175
174
  require_paths:
176
175
  - lib
@@ -185,8 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
184
  - !ruby/object:Gem::Version
186
185
  version: '0'
187
186
  requirements: []
188
- rubygems_version: 3.3.7
189
- signing_key:
187
+ rubygems_version: 4.0.3
190
188
  specification_version: 4
191
189
  summary: A lightweight CLI framework for Ruby
192
190
  test_files: []
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes