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 +4 -4
- data/bin/h-pr +67 -22
- data/lib/hiiro/queue.rb +17 -5
- 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: 0161eeaf4f0cbe1052c13d9373ab9ed268394d1895f498fd5b964ba363b017d9
|
|
4
|
+
data.tar.gz: 559024c602f5667a1ae23b3d9d917295fe676194c17e24489d3047f16cb1b50d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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|
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
infos = batch_fetch_pr_info(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
630
|
-
|
|
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