hiiro 0.1.102 → 0.1.103

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: 8dc2109d62e2122c9143899e72036b38a955878521bdee24d308ca11ee879794
4
- data.tar.gz: 8baf73d8ef12d7f9a28894ba713b44691a01a545ee2e0b1d5dfd14bcfbd87e44
3
+ metadata.gz: 9a10f35b3d066b06834cb59ac651c430314e158326648e7c03857d8619fe98ac
4
+ data.tar.gz: '0281effef29f43bc895ea11c72740c5467a66200ab9d0936ac91db5977ed111a'
5
5
  SHA512:
6
- metadata.gz: d53af01b7a6f6888962d2517ea4272534b67fd29be5b1999afec20430cc64b8a16da67d18796dcbfc0df7f2ab9c176571352541a40e8ef359c3811586bc0f676
7
- data.tar.gz: e1af7c22e7d90bcc1dcf83ab18dd2c48696100446d7677c936a1bb9f5827e6d6bed555ba4ad3d697777c4a0de9811a7cd5337f798b0bcfa3268d9d30b56787aa
6
+ metadata.gz: bfd05cc89b33bddf85607dadfddafa4e5a299309484afe6d402467c220b5cb201f624a9e35ce6fa5f52ff13d3138da41991d56638a9c30f9f76d4369e4965cc6
7
+ data.tar.gz: d5060e6f075475f3f95e48edbadc8dd4cab5d6ea539bbe817d54efd47b417b14c9655e72a04554c86abc9f104754c1cea5379a0eaefe27d97dad3e67e9c1a8ca
data/bin/h-pr CHANGED
@@ -5,6 +5,7 @@ require "time"
5
5
  require "fileutils"
6
6
  require "yaml"
7
7
  require "json"
8
+ require "tempfile"
8
9
 
9
10
  Hiiro.load_env
10
11
 
@@ -254,6 +255,17 @@ class PinnedPRManager
254
255
  JSON.parse(output) rescue []
255
256
  end
256
257
 
258
+ def fetch_my_and_assigned_prs
259
+ authored = `gh pr list --author @me --state open --json number,title,headRefName,url 2>/dev/null`.strip
260
+ assigned = `gh pr list --assignee @me --state open --json number,title,headRefName,url 2>/dev/null`.strip
261
+
262
+ authored_prs = authored.empty? ? [] : (JSON.parse(authored) rescue [])
263
+ assigned_prs = assigned.empty? ? [] : (JSON.parse(assigned) rescue [])
264
+
265
+ # Dedupe by PR number
266
+ (authored_prs + assigned_prs).uniq { |pr| pr['number'] }
267
+ end
268
+
257
269
  def refresh_status(pr)
258
270
  info = fetch_pr_info(pr['number'])
259
271
  return pr unless info
@@ -668,6 +680,261 @@ Hiiro.run(*ARGV, plugins: [Tasks, Tmux, Pins]) do
668
680
  puts "Status updated at #{Time.now.strftime('%H:%M:%S')}"
669
681
  end
670
682
 
683
+ add_subcmd(:update) do
684
+ pinned = pinned_manager.load_pinned
685
+
686
+ if pinned.empty?
687
+ puts "No pinned PRs to update"
688
+ next
689
+ end
690
+
691
+ puts "Updating status for #{pinned.length} PR(s)..."
692
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
693
+ pinned_manager.save_pinned(pinned)
694
+ puts "Done."
695
+ end
696
+
697
+ add_subcmd(:green) do
698
+ pinned = pinned_manager.load_pinned
699
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
700
+ pinned_manager.save_pinned(pinned)
701
+
702
+ filtered = pinned.select { |pr|
703
+ c = pr['checks']
704
+ c && c['failed'] == 0 && c['pending'] == 0 && c['success'] > 0
705
+ }
706
+
707
+ if filtered.empty?
708
+ puts "No PRs with passing checks"
709
+ next
710
+ end
711
+
712
+ puts "PRs with passing checks:"
713
+ puts
714
+ filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
715
+ end
716
+
717
+ add_subcmd(:red) do
718
+ pinned = pinned_manager.load_pinned
719
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
720
+ pinned_manager.save_pinned(pinned)
721
+
722
+ filtered = pinned.select { |pr|
723
+ c = pr['checks']
724
+ c && c['failed'] > 0
725
+ }
726
+
727
+ if filtered.empty?
728
+ puts "No PRs with failing checks"
729
+ next
730
+ end
731
+
732
+ puts "PRs with failing checks:"
733
+ puts
734
+ filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
735
+ end
736
+
737
+ add_subcmd(:old) do
738
+ pinned = pinned_manager.load_pinned
739
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
740
+ pinned_manager.save_pinned(pinned)
741
+
742
+ filtered = pinned.select { |pr| pr['state'] == 'MERGED' }
743
+
744
+ if filtered.empty?
745
+ puts "No merged PRs"
746
+ next
747
+ end
748
+
749
+ puts "Merged PRs:"
750
+ puts
751
+ filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
752
+ end
753
+
754
+ add_subcmd(:prune) do |*args|
755
+ # Alias to auto-unpin
756
+ pinned = pinned_manager.load_pinned
757
+
758
+ if pinned.empty?
759
+ puts "No pinned PRs"
760
+ next
761
+ end
762
+
763
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
764
+
765
+ merged_or_closed = pinned.select { |pr| pr['state'] == 'MERGED' || pr['state'] == 'CLOSED' }
766
+
767
+ if merged_or_closed.empty?
768
+ puts "No merged or closed PRs to prune"
769
+ pinned_manager.save_pinned(pinned)
770
+ next
771
+ end
772
+
773
+ puts "Pruning #{merged_or_closed.length} merged/closed PR(s):"
774
+ puts
775
+
776
+ merged_or_closed.each do |pr|
777
+ puts " ##{pr['number']}: #{pr['title']} [#{pr['state']}]"
778
+ pinned_manager.unpin(pr['number'])
779
+ end
780
+
781
+ puts
782
+ puts "Done. #{pinned_manager.load_pinned.length} PR(s) still tracked."
783
+ end
784
+
785
+ add_subcmd(:draft) do
786
+ pinned = pinned_manager.load_pinned
787
+ pinned.each { |pr| pinned_manager.refresh_status(pr) }
788
+ pinned_manager.save_pinned(pinned)
789
+
790
+ filtered = pinned.select { |pr| pr['is_draft'] == true }
791
+
792
+ if filtered.empty?
793
+ puts "No draft PRs"
794
+ next
795
+ end
796
+
797
+ puts "Draft PRs:"
798
+ puts
799
+ filtered.each_with_index { |pr, i| puts pinned_manager.display_pinned(pr, i) }
800
+ end
801
+
802
+ add_subcmd(:missing) do
803
+ my_prs = pinned_manager.fetch_my_and_assigned_prs
804
+ pinned_numbers = pinned_manager.load_pinned.map { |p| p['number'] }
805
+ missing = my_prs.reject { |pr| pinned_numbers.include?(pr['number']) }
806
+
807
+ if missing.empty?
808
+ puts "All your PRs are tracked"
809
+ next
810
+ end
811
+
812
+ puts "Untracked PRs (#{missing.count}):"
813
+ puts
814
+ missing.each { |pr| puts " ##{pr['number']} [#{pr['headRefName']}] #{pr['title']}" }
815
+ end
816
+
817
+ add_subcmd(:amissing) do
818
+ my_prs = pinned_manager.fetch_my_and_assigned_prs
819
+ pinned_numbers = pinned_manager.load_pinned.map { |p| p['number'] }
820
+ missing = my_prs.reject { |pr| pinned_numbers.include?(pr['number']) }
821
+
822
+ if missing.empty?
823
+ puts "All your PRs are already tracked"
824
+ next
825
+ end
826
+
827
+ missing_numbers = missing.map { |pr| pr['number'] }
828
+
829
+ tmpfile = Tempfile.new(['missing-prs-', '.yml'])
830
+ tmpfile.write(missing_numbers.to_yaml)
831
+ tmpfile.close
832
+
833
+ system(ENV['EDITOR'] || 'nvim', tmpfile.path)
834
+
835
+ to_add = YAML.safe_load_file(tmpfile.path) || []
836
+ tmpfile.unlink
837
+
838
+ added = 0
839
+ to_add.each do |num|
840
+ pr_info = pinned_manager.fetch_pr_info(num)
841
+ next unless pr_info
842
+ pinned_manager.pin(pr_info)
843
+ puts "Tracked: ##{pr_info['number']} #{pr_info['title']}"
844
+ added += 1
845
+ end
846
+
847
+ puts
848
+ puts "Added #{added} PR(s) to tracking."
849
+ end
850
+
851
+ add_subcmd(:track) do
852
+ pr_info = pinned_manager.fetch_current_branch_pr
853
+ unless pr_info
854
+ puts "No PR for current branch"
855
+ next
856
+ end
857
+
858
+ if pinned_manager.pinned?(pr_info['number'])
859
+ puts "Already tracking ##{pr_info['number']}"
860
+ next
861
+ end
862
+
863
+ full_info = pinned_manager.fetch_pr_info(pr_info['number'])
864
+ pinned_manager.pin(full_info)
865
+ puts "Now tracking ##{pr_info['number']}: #{pr_info['title']}"
866
+ end
867
+
868
+ add_subcmd(:ignore) do |ref = nil|
869
+ # Alias to unpin
870
+ pinned = pinned_manager.load_pinned
871
+
872
+ if pinned.empty?
873
+ puts "No tracked PRs"
874
+ next
875
+ end
876
+
877
+ pr_number = nil
878
+
879
+ if ref.nil?
880
+ lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
881
+ h[pinned_manager.display_pinned(pr, idx)] = pr['number'].to_s
882
+ end
883
+
884
+ pr_number = fuzzyfind_from_map(lines)
885
+ unless pr_number
886
+ puts "No PR selected"
887
+ next
888
+ end
889
+ elsif ref =~ /^\d+$/
890
+ pr_number = ref
891
+ else
892
+ puts "Usage: h pr ignore [PR_NUMBER]"
893
+ next
894
+ end
895
+
896
+ if pinned_manager.unpin(pr_number)
897
+ puts "Stopped tracking PR ##{pr_number}"
898
+ else
899
+ puts "PR ##{pr_number} was not tracked"
900
+ end
901
+ end
902
+
903
+ add_subcmd(:rm) do |ref = nil|
904
+ # Alias to ignore/unpin
905
+ pinned = pinned_manager.load_pinned
906
+
907
+ if pinned.empty?
908
+ puts "No tracked PRs"
909
+ next
910
+ end
911
+
912
+ pr_number = nil
913
+
914
+ if ref.nil?
915
+ lines = pinned.each_with_index.each_with_object({}) do |(pr, idx), h|
916
+ h[pinned_manager.display_pinned(pr, idx)] = pr['number'].to_s
917
+ end
918
+
919
+ pr_number = fuzzyfind_from_map(lines)
920
+ unless pr_number
921
+ puts "No PR selected"
922
+ next
923
+ end
924
+ elsif ref =~ /^\d+$/
925
+ pr_number = ref
926
+ else
927
+ puts "Usage: h pr rm [PR_NUMBER]"
928
+ next
929
+ end
930
+
931
+ if pinned_manager.unpin(pr_number)
932
+ puts "Removed PR ##{pr_number} from tracking"
933
+ else
934
+ puts "PR ##{pr_number} was not tracked"
935
+ end
936
+ end
937
+
671
938
  add_subcmd(:'suggest-unpin') do |*args|
672
939
  pinned = pinned_manager.load_pinned
673
940
 
data/bin/h-session CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'hiiro'
4
+ require 'tempfile'
5
+ require 'yaml'
4
6
 
5
- Hiiro.run(*ARGV, plugins: [Pins]) do
7
+ Hiiro.run(*ARGV, plugins: [Pins, Tasks]) do
6
8
  tmux = tmux_client
7
9
 
8
10
  add_subcmd(:ls, :list) { |*args|
@@ -126,4 +128,45 @@ Hiiro.run(*ARGV, plugins: [Pins]) do
126
128
  puts "Copied session '#{selected}' to clipboard"
127
129
  end
128
130
  end
131
+
132
+ add_subcmd(:orphans) do
133
+ env = Environment.current
134
+ task_session_names = env.all_tasks.map(&:session_name)
135
+ orphans = env.all_sessions.reject { |s| task_session_names.include?(s.name) }
136
+
137
+ if orphans.empty?
138
+ puts "No orphan sessions"
139
+ next
140
+ end
141
+
142
+ puts "Orphan sessions (#{orphans.count}):"
143
+ orphans.each { |s| puts " #{s.name}" }
144
+ end
145
+
146
+ add_subcmd(:okill) do
147
+ env = Environment.current
148
+ task_session_names = env.all_tasks.map(&:session_name)
149
+ orphans = env.all_sessions.reject { |s| task_session_names.include?(s.name) }
150
+
151
+ if orphans.empty?
152
+ puts "No orphan sessions to kill"
153
+ next
154
+ end
155
+
156
+ orphan_names = orphans.map(&:name)
157
+
158
+ tmpfile = Tempfile.new(['orphan-sessions-', '.yml'])
159
+ tmpfile.write(orphan_names.to_yaml)
160
+ tmpfile.close
161
+
162
+ system(ENV['EDITOR'] || 'nvim', tmpfile.path)
163
+
164
+ to_kill = YAML.safe_load_file(tmpfile.path) || []
165
+ tmpfile.unlink
166
+
167
+ to_kill.each do |name|
168
+ tmux.kill_session(name)
169
+ puts "Killed: #{name}"
170
+ end
171
+ end
129
172
  end
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.102"
2
+ VERSION = "0.1.103"
3
3
  end
data/plugins/tasks.rb CHANGED
@@ -646,7 +646,7 @@ class TaskManager
646
646
  h[line] = val
647
647
  end
648
648
 
649
- o.fuzzyfind_from_map(mapping)
649
+ hiiro.fuzzyfind_from_map(mapping)
650
650
  end
651
651
 
652
652
  def select_branch_interactive(prompt = nil)
@@ -657,7 +657,7 @@ class TaskManager
657
657
  end
658
658
  return nil if name_map.empty?
659
659
 
660
- o.fuzzyfind_from_map(name_map)
660
+ hiiro.fuzzyfind_from_map(name_map)
661
661
  end
662
662
 
663
663
  # --- Private helpers ---
@@ -906,6 +906,30 @@ module Tasks
906
906
  print tm.value_for_task(task_name, &:session_name)
907
907
  end
908
908
 
909
+ h.add_subcmd(:current) do
910
+ task = tm.current_task
911
+ next STDERR.puts("Not in a task") unless task
912
+ print task.name
913
+ end
914
+
915
+ h.add_subcmd(:cbranch) do
916
+ task = tm.current_task
917
+ next STDERR.puts("Not in a task") unless task
918
+ print task.branch if task.branch
919
+ end
920
+
921
+ h.add_subcmd(:ctree) do
922
+ task = tm.current_task
923
+ next STDERR.puts("Not in a task") unless task
924
+ print task.tree_name if task.tree_name
925
+ end
926
+
927
+ h.add_subcmd(:csession) do
928
+ task = tm.current_task
929
+ next STDERR.puts("Not in a task") unless task
930
+ print task.session_name if task.session_name
931
+ end
932
+
909
933
  h.add_subcmd(:status) { tm.status }
910
934
  h.add_subcmd(:st) { tm.status }
911
935
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiiro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.102
4
+ version: 0.1.103
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-20 00:00:00.000000000 Z
11
+ date: 2026-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry