hiiro 0.1.199 → 0.1.201

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: 0b00244bcf091a676a118452abc560dddc2ada10cdd940712347116ae28a19c0
4
- data.tar.gz: e9d04032c1de3e6ffbaad800c702167316dd3231d485985e9c809830c7b26e67
3
+ metadata.gz: 0161eeaf4f0cbe1052c13d9373ab9ed268394d1895f498fd5b964ba363b017d9
4
+ data.tar.gz: 559024c602f5667a1ae23b3d9d917295fe676194c17e24489d3047f16cb1b50d
5
5
  SHA512:
6
- metadata.gz: 05e222b01ba1adefd31726d4b4095aaa704f09260ee3ff0e7fa27a42abf121099816c3d9a34b5c2acc785f64502ae0bd43baa8a48da87f6cfa94df30b14f3a8f
7
- data.tar.gz: e971605f3f44578d3dd39462a48a39dc94d09741f5c196aa119bb36db9df8d23c3cc1c3b3122c2f35719517d6f16994f14806ac34d1cd8d355256ed7bff98f41
6
+ metadata.gz: 85941f5cf69ef053cc6ae8a08ef2b1036313ee8b60413243009cbd85311c2fcb8823b52c11780dd7b4fdf2785027440a86373146673dbb76e9ab2b3de819d773
7
+ data.tar.gz: 8cba796136692b6cd26cc372a42d429d422a7a141414939cc383212f4fa9a491b1a94a05cb0383fc2db27d503c4d63ad00be87bd49c63e08b52b5e4458acc3a6
data/bin/h-pr CHANGED
@@ -174,6 +174,15 @@ end
174
174
  class PinnedPRManager
175
175
  PINNED_FILE = File.join(Dir.home, '.config/hiiro/pinned_prs.yml')
176
176
 
177
+ def self.repo_from_url(url)
178
+ return nil unless url
179
+ url.match(%r{github\.com/([^/]+/[^/]+)/pull/})&.[](1)
180
+ end
181
+
182
+ def pr_repo(pr)
183
+ pr['repo'] || self.class.repo_from_url(pr['url'])
184
+ end
185
+
177
186
  def initialize
178
187
  ensure_file
179
188
  end
@@ -195,8 +204,12 @@ class PinnedPRManager
195
204
  end
196
205
 
197
206
  def pin(pr_info)
207
+ pr_info['repo'] ||= self.class.repo_from_url(pr_info['url'])
208
+
198
209
  pinned = load_pinned
199
- existing = pinned.find { |p| p['number'] == pr_info['number'] }
210
+ existing = pinned.find { |p|
211
+ p['number'] == pr_info['number'] && pr_repo(p) == pr_repo(pr_info)
212
+ }
200
213
 
201
214
  if existing
202
215
  existing.merge!(pr_info)
@@ -221,11 +234,14 @@ class PinnedPRManager
221
234
  load_pinned.any? { |p| p['number'].to_s == pr_number.to_s }
222
235
  end
223
236
 
224
- def fetch_pr_info(pr_number)
237
+ def fetch_pr_info(pr_number, repo: nil)
225
238
  fields = 'number,title,url,headRefName,state,statusCheckRollup,reviewDecision,reviews,isDraft,mergeable'
226
- output = `gh pr view #{pr_number} --json #{fields} 2>/dev/null`.strip
239
+ repo_flag = repo ? " --repo #{repo}" : ""
240
+ output = `gh pr view #{pr_number}#{repo_flag} --json #{fields} 2>/dev/null`.strip
227
241
  return nil if output.empty?
228
- JSON.parse(output)
242
+ result = JSON.parse(output)
243
+ result['repo'] ||= self.class.repo_from_url(result['url'])
244
+ result
229
245
  rescue JSON::ParserError
230
246
  nil
231
247
  end
@@ -234,7 +250,9 @@ class PinnedPRManager
234
250
  fields = 'number,title,url,headRefName,state'
235
251
  output = `gh pr view --json #{fields} 2>/dev/null`.strip
236
252
  return nil if output.empty?
237
- JSON.parse(output)
253
+ result = JSON.parse(output)
254
+ result['repo'] ||= self.class.repo_from_url(result['url'])
255
+ result
238
256
  rescue JSON::ParserError
239
257
  nil
240
258
  end
@@ -242,13 +260,15 @@ class PinnedPRManager
242
260
  def fetch_my_prs
243
261
  output = `gh pr list --author @me --state open --json number,title,headRefName,url 2>/dev/null`.strip
244
262
  return [] if output.empty?
245
- JSON.parse(output) rescue []
263
+ prs = JSON.parse(output) rescue []
264
+ prs.each { |pr| pr['repo'] ||= self.class.repo_from_url(pr['url']) }
246
265
  end
247
266
 
248
267
  def fetch_assigned_prs
249
268
  output = `gh pr list --assignee @me --state open --json number,title,headRefName,url 2>/dev/null`.strip
250
269
  return [] if output.empty?
251
- JSON.parse(output) rescue []
270
+ prs = JSON.parse(output) rescue []
271
+ prs.each { |pr| pr['repo'] ||= self.class.repo_from_url(pr['url']) }
252
272
  end
253
273
 
254
274
  def fetch_my_and_assigned_prs
@@ -274,10 +294,27 @@ class PinnedPRManager
274
294
  end
275
295
 
276
296
 
277
- def batch_fetch_pr_info(pr_numbers)
297
+ # Accepts an array of PR records (each with 'number' and optionally 'repo'/'url'),
298
+ # groups them by repo, and fetches in batches per repo via GraphQL.
299
+ def batch_fetch_pr_info(prs)
300
+ return {} if prs.empty?
301
+
302
+ by_repo = prs.group_by { |pr| pr_repo(pr) || 'instacart/carrot' }
303
+
304
+ result = {}
305
+ by_repo.each do |repo_path, repo_prs|
306
+ owner, name = repo_path.split('/', 2)
307
+ pr_numbers = repo_prs.map { |pr| pr['number'] }
308
+ result.merge!(fetch_batch_for_repo(owner, name, pr_numbers))
309
+ end
310
+ result
311
+ end
312
+
313
+ private
314
+
315
+ def fetch_batch_for_repo(owner, name, pr_numbers)
278
316
  return {} if pr_numbers.empty?
279
317
 
280
- # Build GraphQL query to fetch multiple PRs at once
281
318
  pr_queries = pr_numbers.map.with_index do |num, idx|
282
319
  <<~GRAPHQL.strip
283
320
  pr#{idx}: pullRequest(number: #{num}) {
@@ -316,7 +353,7 @@ class PinnedPRManager
316
353
 
317
354
  query = <<~GRAPHQL
318
355
  query {
319
- repository(owner: "instacart", name: "carrot") {
356
+ repository(owner: "#{owner}", name: "#{name}") {
320
357
  #{pr_queries.join("\n")}
321
358
  }
322
359
  }
@@ -329,14 +366,13 @@ class PinnedPRManager
329
366
  repo_data = data.dig('data', 'repository')
330
367
  return {} unless repo_data
331
368
 
332
- # Convert GraphQL response to hash keyed by PR number
333
- pr_info_by_number = {}
369
+ pr_info_by_key = {}
370
+ repo_path = "#{owner}/#{name}"
334
371
  pr_numbers.each_with_index do |num, idx|
335
372
  pr_data = repo_data["pr#{idx}"]
336
373
  next unless pr_data
337
374
 
338
- # Transform GraphQL response to match gh pr view format
339
- pr_info_by_number[num] = {
375
+ pr_info_by_key[[num, repo_path]] = {
340
376
  'number' => pr_data['number'],
341
377
  'title' => pr_data['title'],
342
378
  'url' => pr_data['url'],
@@ -346,15 +382,18 @@ class PinnedPRManager
346
382
  'mergeable' => pr_data['mergeable'],
347
383
  'reviewDecision' => pr_data['reviewDecision'],
348
384
  'statusCheckRollup' => pr_data.dig('statusCheckRollup', 'contexts', 'nodes'),
349
- 'reviews' => pr_data.dig('reviews', 'nodes') || []
385
+ 'reviews' => pr_data.dig('reviews', 'nodes') || [],
386
+ 'repo' => repo_path
350
387
  }
351
388
  end
352
389
 
353
- pr_info_by_number
390
+ pr_info_by_key
354
391
  rescue JSON::ParserError, StandardError
355
392
  {}
356
393
  end
357
394
 
395
+ public
396
+
358
397
  def refresh_all_status(prs, force: false)
359
398
  prs_to_refresh = prs.select { |pr| needs_refresh?(pr, force: force) }
360
399
 
@@ -363,11 +402,11 @@ class PinnedPRManager
363
402
  return prs
364
403
  end
365
404
 
366
- pr_numbers = prs_to_refresh.map { |pr| pr['number'] }
367
- infos = batch_fetch_pr_info(pr_numbers)
405
+ # infos is keyed by [number, repo] to avoid collisions across repos
406
+ infos = batch_fetch_pr_info(prs_to_refresh)
368
407
 
369
408
  prs_to_refresh.each do |pr|
370
- info = infos[pr['number']]
409
+ info = infos[[pr['number'], pr_repo(pr) || 'instacart/carrot']]
371
410
  next unless info
372
411
 
373
412
  pr['state'] = info['state']
@@ -386,7 +425,7 @@ class PinnedPRManager
386
425
  def refresh_status(pr, force: false)
387
426
  return pr unless needs_refresh?(pr, force: force)
388
427
 
389
- info = fetch_pr_info(pr['number'])
428
+ info = fetch_pr_info(pr['number'], repo: pr_repo(pr))
390
429
  return pr unless info
391
430
 
392
431
  pr['state'] = info['state']
@@ -492,7 +531,10 @@ class PinnedPRManager
492
531
  ""
493
532
  end
494
533
 
495
- "#{num} #{state_icon} ##{pr['number']} #{pr['title']}#{checks_str}#{reviews_str}".strip
534
+ repo = pr_repo(pr)
535
+ repo_label = (repo && repo != 'instacart/carrot') ? " [#{repo}]" : ""
536
+
537
+ "#{num} #{state_icon} ##{pr['number']}#{repo_label} #{pr['title']}#{checks_str}#{reviews_str}".strip
496
538
  end
497
539
 
498
540
  def display_detailed(pr, idx = nil)
@@ -505,7 +547,10 @@ class PinnedPRManager
505
547
  else pr['is_draft'] ? 'DRAFT' : 'OPEN'
506
548
  end
507
549
 
508
- lines << "#{num} ##{pr['number']} - #{pr['title']}"
550
+ repo = pr_repo(pr)
551
+ repo_label = (repo && repo != 'instacart/carrot') ? " [#{repo}]" : ""
552
+
553
+ lines << "#{num} ##{pr['number']}#{repo_label} - #{pr['title']}"
509
554
  lines << " State: #{state_str}"
510
555
  lines << " Branch: #{pr['headRefName']}" if pr['headRefName']
511
556
  lines << " URL: #{pr['url']}" if pr['url']
data/lib/hiiro/queue.rb CHANGED
@@ -97,7 +97,7 @@ class Hiiro
97
97
 
98
98
  if prompt_obj
99
99
  if prompt_obj.task
100
- target_session = prompt_obj.task.session_name
100
+ target_session = prompt_obj.session_name
101
101
  tree = prompt_obj.task.tree
102
102
  working_dir = tree.path if tree
103
103
  elsif prompt_obj.session
@@ -618,17 +618,29 @@ class Hiiro
618
618
  end
619
619
 
620
620
  attr_reader :hiiro, :doc, :frontmatter, :prompt
621
- attr_reader :task_name, :tree_name, :session_name
622
621
 
623
622
  def initialize(doc, hiiro: nil)
624
623
  @hiiro = hiiro
625
624
  @doc = doc
626
625
  @frontmatter = doc.front_matter
627
626
  @prompt = prompt
627
+ end
628
+
629
+ def task_name
630
+ doc.front_matter['task_name']
631
+ end
632
+
633
+ def tree_name
634
+ doc.front_matter['tree_name'].tap do |tname|
635
+ puts tree_name: tname
636
+ puts task_tree: task&.tree
637
+ puts task_tree_name: task&.tree_name
638
+ puts task_tree_path: task&.tree&.path
639
+ end
640
+ end
628
641
 
629
- @task_name = doc.front_matter['task_name']
630
- @tree_name = doc.front_matter['tree_name']
631
- @session_name = doc.front_matter['session_name']
642
+ def session_name
643
+ doc.front_matter['session_name'] || task&.session_name
632
644
  end
633
645
 
634
646
  def task
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.199"
2
+ VERSION = "0.1.201"
3
3
  end
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.199
4
+ version: 0.1.201
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota