hiiro 0.1.57 → 0.1.59

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: 7f55785ca9b54838e9b0e0eefe8e1e829f85ad1b1d9a1ed6b961305b847f1828
4
- data.tar.gz: cda5ec5bdc86b4210e4870d88bca437c9cc7dbac3857ae3e264a66ba0b054675
3
+ metadata.gz: 48a2bf79d71ec28000cfde49b21206385dc42c12f4dc6a907d5b9488a9c4e6db
4
+ data.tar.gz: 9dfac1b2b9d320b7c336383ec101044180cd3220365df3537cdd418e753522f9
5
5
  SHA512:
6
- metadata.gz: 8d8e719178da0355245154216264edb4c292fb5ccc62dc561e8529bf7b67f4a94d85a5db3516e1406f5cf7464f61454dc3c07f7dc26530a44a895577b7082c12
7
- data.tar.gz: 28a570aba442cb90419feaf3edd4ef4135773fc2f971401df942bb09617f17c7466c3ba32dbfc2edd1849895d973cdada07f80b8edd2932685e8f21bc00f29d4
6
+ metadata.gz: '0487e36729f6d2d3d1269a89e2a76d59b959ea94f6166732439e07fe7eef363dd36741993852c9a95d6302bb6675ea8a8bc8c32517bec9e503e2a5610f88f47e'
7
+ data.tar.gz: d3a5ba6ab09f95f8fb46be7ff55428589d8a3129df025dcddf9b0d879246606679ef892df7bf0208052ddc72e620c1d1681cbe5218f338b37210429fb62261fe
data/bin/h-branch CHANGED
@@ -225,6 +225,150 @@ hiiro.add_subcmd(:select) do |*args|
225
225
  end
226
226
  end
227
227
 
228
+ hiiro.add_subcmd(:checkout, :co) { |branch = nil, *args|
229
+ unless branch
230
+ # Use sk to select a branch
231
+ branches = hiiro.git.branches(sort_by: 'authordate', ignore_case: true)
232
+ require 'open3'
233
+ selected, status = Open3.capture2('sk', '--no-sort', '--tac', stdin_data: branches.join("\n"))
234
+ unless status.success? && !selected.strip.empty?
235
+ puts "No branch selected"
236
+ next
237
+ end
238
+ branch = selected.strip
239
+ end
240
+
241
+ system('git', 'checkout', branch, *args)
242
+ }
243
+
244
+ hiiro.add_subcmd(:rm, :remove) { |branch = nil, *args|
245
+ unless branch
246
+ # Use sk to select a branch
247
+ branches = hiiro.git.branches(sort_by: 'authordate', ignore_case: true)
248
+ require 'open3'
249
+ selected, status = Open3.capture2('sk', '--no-sort', '--tac', stdin_data: branches.join("\n"))
250
+ unless status.success? && !selected.strip.empty?
251
+ puts "No branch selected"
252
+ next
253
+ end
254
+ branch = selected.strip
255
+ end
256
+
257
+ system('git', 'branch', '-d', branch, *args)
258
+ }
259
+
260
+ hiiro.add_subcmd(:duplicate) { |new_name = nil, source = nil|
261
+ unless new_name
262
+ puts "Usage: h branch duplicate <new_name> [source_branch]"
263
+ puts " Creates a copy of source_branch (or current branch) as new_name"
264
+ next
265
+ end
266
+
267
+ source ||= hiiro.git.branch
268
+ system('git', 'branch', new_name, source)
269
+ puts "Created branch '#{new_name}' from '#{source}'"
270
+ }
271
+
272
+ hiiro.add_subcmd(:push) { |*args|
273
+ opts = Hiiro::Options.new(args) do
274
+ option :remote, short: 'r', default: 'origin', desc: 'Remote name'
275
+ option :from, short: 'f', desc: 'Local branch or commit to push'
276
+ option :to, short: 't', desc: 'Remote branch name'
277
+ flag :force, short: 'F', desc: 'Force push'
278
+ flag :set_upstream, short: 'u', desc: 'Set upstream tracking'
279
+ end.parse!
280
+
281
+ local_ref = opts[:from] || hiiro.git.branch
282
+ remote_branch = opts[:to] || local_ref
283
+
284
+ git_args = ['git', 'push']
285
+ git_args << '--force' if opts[:force]
286
+ git_args << '-u' if opts[:set_upstream]
287
+ git_args << opts[:remote]
288
+ git_args << "#{local_ref}:#{remote_branch}"
289
+
290
+ puts "Pushing #{local_ref} to #{opts[:remote]}/#{remote_branch}..."
291
+ system(*git_args)
292
+ }
293
+
294
+ hiiro.add_subcmd(:diff) { |*args|
295
+ case args.length
296
+ when 0
297
+ # Compare current branch to main/master
298
+ base = %w[main master].find { |b| system("git rev-parse --verify #{b} >/dev/null 2>&1") } || 'HEAD~1'
299
+ range = "#{base}..HEAD"
300
+ when 1
301
+ # Compare current branch to specified ref
302
+ range = "#{args[0]}..HEAD"
303
+ else
304
+ # Compare two refs (from..to or from...to based on args)
305
+ from, to = args[0..1]
306
+ range = "#{from}..#{to}"
307
+ end
308
+
309
+ system('git', 'log', '--oneline', '--decorate', range)
310
+ }
311
+
312
+ hiiro.add_subcmd(:ahead) { |*args|
313
+ case args.length
314
+ when 0
315
+ base = %w[main master].find { |b| system("git rev-parse --verify #{b} >/dev/null 2>&1") } || 'HEAD~1'
316
+ ahead_of = base
317
+ branch = 'HEAD'
318
+ when 1
319
+ ahead_of = args[0]
320
+ branch = 'HEAD'
321
+ else
322
+ ahead_of, branch = args[0..1]
323
+ end
324
+
325
+ count = `git rev-list --count #{ahead_of}..#{branch}`.strip.to_i
326
+ puts "#{branch} is #{count} commit(s) ahead of #{ahead_of}"
327
+ }
328
+
329
+ hiiro.add_subcmd(:behind) { |*args|
330
+ case args.length
331
+ when 0
332
+ base = %w[main master].find { |b| system("git rev-parse --verify #{b} >/dev/null 2>&1") } || 'HEAD~1'
333
+ behind_of = base
334
+ branch = 'HEAD'
335
+ when 1
336
+ behind_of = args[0]
337
+ branch = 'HEAD'
338
+ else
339
+ behind_of, branch = args[0..1]
340
+ end
341
+
342
+ count = `git rev-list --count #{branch}..#{behind_of}`.strip.to_i
343
+ puts "#{branch} is #{count} commit(s) behind #{behind_of}"
344
+ }
345
+
346
+ hiiro.add_subcmd(:ancestor) { |*args|
347
+ case args.length
348
+ when 0
349
+ # Check if main/master is ancestor of HEAD
350
+ base = %w[main master].find { |b| system("git rev-parse --verify #{b} >/dev/null 2>&1") }
351
+ unless base
352
+ puts "Cannot find main or master branch"
353
+ next
354
+ end
355
+ ancestor = base
356
+ descendant = 'HEAD'
357
+ when 1
358
+ # Check if arg is ancestor of HEAD
359
+ ancestor = args[0]
360
+ descendant = 'HEAD'
361
+ else
362
+ ancestor, descendant = args[0..1]
363
+ end
364
+
365
+ if system("git merge-base --is-ancestor #{ancestor} #{descendant}")
366
+ puts "Yes, #{ancestor} is an ancestor of #{descendant}"
367
+ else
368
+ puts "No, #{ancestor} is NOT an ancestor of #{descendant}"
369
+ end
370
+ }
371
+
228
372
  hiiro.add_default { manager.help }
229
373
 
230
374
  hiiro.run
data/bin/h-buffer CHANGED
@@ -2,8 +2,12 @@
2
2
 
3
3
  require "hiiro"
4
4
 
5
+ def buffer_names
6
+ `tmux list-buffers -F '\#{buffer_name}'`.strip.split("\n")
7
+ end
8
+
5
9
  def find_buffer(partial)
6
- buffer_list = `tmux list-buffers`.lines.map{|line| line.split(/:/, 2).first }
10
+ buffer_list = buffer_names
7
11
 
8
12
  buffer, *other = buffer_list.grep(Regexp.new(partial))
9
13
 
@@ -12,18 +16,57 @@ def find_buffer(partial)
12
16
  buffer
13
17
  end
14
18
 
19
+ def select_buffer(partial = nil)
20
+ names = buffer_names
21
+ return nil if names.empty?
22
+
23
+ if partial
24
+ names = names.grep(Regexp.new(partial))
25
+ end
26
+
27
+ return nil if names.empty?
28
+ return names.first if names.length == 1
29
+
30
+ require 'open3'
31
+ selected, status = Open3.capture2('sk', '--no-sort', stdin_data: names.join("\n"))
32
+ return nil unless status.success?
33
+
34
+ selected.strip.empty? ? nil : selected.strip
35
+ end
36
+
15
37
  o = Hiiro.init(*ARGV, plugins: [Pins])
16
38
 
17
39
  o.add_subcmd(:ls) { |*args|
18
40
  system('tmux', 'list-buffers', *args)
19
41
  }
20
42
 
21
- o.add_subcmd(:show) { |name, *args|
22
- buffer = find_buffer(name)
43
+ o.add_subcmd(:show) { |name = nil, *args|
44
+ buffer = select_buffer(name)
45
+ unless buffer
46
+ puts "No buffer selected or found"
47
+ next
48
+ end
23
49
 
24
50
  system('tmux', 'show-buffer', '-b', buffer, *args)
25
51
  }
26
52
 
53
+ o.add_subcmd(:copy) { |name = nil, *args|
54
+ buffer = select_buffer(name)
55
+ unless buffer
56
+ puts "No buffer selected or found"
57
+ next
58
+ end
59
+
60
+ require 'open3'
61
+ content = `tmux show-buffer -b #{buffer}`
62
+ Open3.popen3('pbcopy') do |stdin, stdout, stderr, wait_thr|
63
+ stdin.write(content)
64
+ stdin.close
65
+ wait_thr.value
66
+ end
67
+ puts "Copied buffer '#{buffer}' to clipboard"
68
+ }
69
+
27
70
  o.add_subcmd(:save) { |*args|
28
71
  system('tmux', 'save-buffer', *args)
29
72
  }
data/bin/h-commit ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "hiiro"
4
+
5
+ hiiro = Hiiro.init(*ARGV, plugins: [Pins])
6
+
7
+ hiiro.add_subcmd(:edit) { system(ENV['EDITOR'] || 'nvim', __FILE__) }
8
+
9
+ hiiro.add_subcmd(:select, :sk) { |*args|
10
+ # Pass all args to git log, with sensible defaults
11
+ git_args = ['git', 'log', '--oneline', '--decorate', '-n', '50']
12
+ git_args.concat(args) if args.any?
13
+
14
+ commits = `#{git_args.shelljoin}`.lines.map(&:chomp)
15
+
16
+ if commits.empty?
17
+ puts "No commits found"
18
+ next
19
+ end
20
+
21
+ require 'open3'
22
+ selected, status = Open3.capture2('sk', '--no-sort', stdin_data: commits.join("\n"))
23
+
24
+ if status.success? && !selected.strip.empty?
25
+ # Extract just the SHA from the selected line
26
+ sha = selected.strip.split.first
27
+ puts sha
28
+ end
29
+ }
30
+
31
+ hiiro.add_default {
32
+ puts "Usage: h commit <subcommand> [args]"
33
+ puts
34
+ puts "Subcommands:"
35
+ puts " select, sk Select a commit using sk and print its SHA"
36
+ puts " Additional args are passed to git log"
37
+ }
38
+
39
+ hiiro.run
@@ -1,5 +1,112 @@
1
1
  class Hiiro
2
2
  class PrefixMatcher
3
+ class << self
4
+ def find(items, prefix, key: nil, &block)
5
+ new(items, key, &block).find(prefix)
6
+ end
7
+
8
+ def find_all(items, prefix, key: nil, &block)
9
+ new(items, key, &block).find_all(prefix)
10
+ end
11
+
12
+ def resolve(items, prefix, key: nil, &block)
13
+ new(items, key, &block).resolve(prefix)
14
+ end
15
+
16
+ def find_path(items, prefix, key: nil, &block)
17
+ new(items, key, &block).find_path(prefix)
18
+ end
19
+
20
+ def find_all_paths(items, prefix, key: nil, &block)
21
+ new(items, key, &block).find_all_paths(prefix)
22
+ end
23
+
24
+ def resolve_path(items, prefix, key: nil, &block)
25
+ new(items, key, &block).resolve_path(prefix)
26
+ end
27
+ end
28
+
29
+ attr_reader :original_items, :key, :block
30
+
31
+ def initialize(items, key = nil, &block)
32
+ @original_items = items
33
+ @key = key
34
+ @block = block
35
+ end
36
+
37
+ def all_items(key = nil, &block)
38
+ use_key = key.nil? && !block_given? ? @key : key
39
+ use_block = key.nil? && !block_given? ? @block : block
40
+
41
+ @all_items_cache ||= {}
42
+ cache_key = [use_key, use_block].hash
43
+
44
+ @all_items_cache[cache_key] ||= original_items.map { |item|
45
+ Item.new(
46
+ item: item,
47
+ extracted_item: extract(item, use_key, &use_block),
48
+ key: use_key,
49
+ block: use_block
50
+ )
51
+ }
52
+ end
53
+
54
+ def search(prefix, key = nil, &block)
55
+ Result.new(
56
+ matcher: self,
57
+ all_items: all_items(key, &block),
58
+ prefix: prefix,
59
+ key: key || @key,
60
+ block: block || @block
61
+ )
62
+ end
63
+
64
+ def find(prefix, key = nil, &block)
65
+ search(prefix, key, &block)
66
+ end
67
+
68
+ def find_all(prefix, key = nil, &block)
69
+ search(prefix, key, &block)
70
+ end
71
+
72
+ def resolve(prefix, key = nil, &block)
73
+ search(prefix, key, &block)
74
+ end
75
+
76
+ def find_path(prefix, key = nil, &block)
77
+ search_path(prefix, key, &block)
78
+ end
79
+
80
+ def find_all_paths(prefix, key = nil, &block)
81
+ search_path(prefix, key, &block)
82
+ end
83
+
84
+ def resolve_path(prefix, key = nil, &block)
85
+ search_path(prefix, key, &block)
86
+ end
87
+
88
+ def search_path(prefix, key = nil, &block)
89
+ PathResult.new(
90
+ matcher: self,
91
+ all_items: all_items(key, &block),
92
+ prefix: prefix,
93
+ key: key || @key,
94
+ block: block || @block
95
+ )
96
+ end
97
+
98
+ private
99
+
100
+ def matches?(item, prefix)
101
+ item.to_s.start_with?(prefix.to_s)
102
+ end
103
+
104
+ def extract(item, key = nil, &block)
105
+ return block.call(item) if block
106
+ return item.send(key) if key
107
+ item
108
+ end
109
+
3
110
  class Item
4
111
  attr_reader :item, :extracted_item, :key, :block
5
112
 
@@ -130,112 +237,5 @@ class Hiiro
130
237
  matches.first
131
238
  end
132
239
  end
133
-
134
- class << self
135
- def find(items, prefix, key: nil, &block)
136
- new(items, key, &block).find(prefix)
137
- end
138
-
139
- def find_all(items, prefix, key: nil, &block)
140
- new(items, key, &block).find_all(prefix)
141
- end
142
-
143
- def resolve(items, prefix, key: nil, &block)
144
- new(items, key, &block).resolve(prefix)
145
- end
146
-
147
- def find_path(items, prefix, key: nil, &block)
148
- new(items, key, &block).find_path(prefix)
149
- end
150
-
151
- def find_all_paths(items, prefix, key: nil, &block)
152
- new(items, key, &block).find_all_paths(prefix)
153
- end
154
-
155
- def resolve_path(items, prefix, key: nil, &block)
156
- new(items, key, &block).resolve_path(prefix)
157
- end
158
- end
159
-
160
- attr_reader :original_items, :key, :block
161
-
162
- def initialize(items, key = nil, &block)
163
- @original_items = items
164
- @key = key
165
- @block = block
166
- end
167
-
168
- def all_items(key = nil, &block)
169
- use_key = key.nil? && !block_given? ? @key : key
170
- use_block = key.nil? && !block_given? ? @block : block
171
-
172
- @all_items_cache ||= {}
173
- cache_key = [use_key, use_block].hash
174
-
175
- @all_items_cache[cache_key] ||= original_items.map { |item|
176
- Item.new(
177
- item: item,
178
- extracted_item: extract(item, use_key, &use_block),
179
- key: use_key,
180
- block: use_block
181
- )
182
- }
183
- end
184
-
185
- def search(prefix, key = nil, &block)
186
- Result.new(
187
- matcher: self,
188
- all_items: all_items(key, &block),
189
- prefix: prefix,
190
- key: key || @key,
191
- block: block || @block
192
- )
193
- end
194
-
195
- def find(prefix, key = nil, &block)
196
- search(prefix, key, &block)
197
- end
198
-
199
- def find_all(prefix, key = nil, &block)
200
- search(prefix, key, &block)
201
- end
202
-
203
- def resolve(prefix, key = nil, &block)
204
- search(prefix, key, &block)
205
- end
206
-
207
- def find_path(prefix, key = nil, &block)
208
- search_path(prefix, key, &block)
209
- end
210
-
211
- def find_all_paths(prefix, key = nil, &block)
212
- search_path(prefix, key, &block)
213
- end
214
-
215
- def resolve_path(prefix, key = nil, &block)
216
- search_path(prefix, key, &block)
217
- end
218
-
219
- def search_path(prefix, key = nil, &block)
220
- PathResult.new(
221
- matcher: self,
222
- all_items: all_items(key, &block),
223
- prefix: prefix,
224
- key: key || @key,
225
- block: block || @block
226
- )
227
- end
228
-
229
- private
230
-
231
- def matches?(item, prefix)
232
- item.to_s.start_with?(prefix.to_s)
233
- end
234
-
235
- def extract(item, key = nil, &block)
236
- return block.call(item) if block
237
- return item.send(key) if key
238
- item
239
- end
240
240
  end
241
241
  end
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.57"
2
+ VERSION = "0.1.59"
3
3
  end
data/lib/hiiro.rb CHANGED
@@ -87,6 +87,12 @@ class Hiiro
87
87
  ].map(&:shellescape).join(' ')
88
88
  end
89
89
 
90
+ def make_child(subcmd, *args, **kwargs, &block)
91
+ bin_name = [bin, subcmd.to_s].join(?-)
92
+
93
+ Hiiro.init(bin_name, *args, **kwargs, &block)
94
+ end
95
+
90
96
  def history
91
97
  @history ||= History.new
92
98
  end
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.57
4
+ version: 0.1.59
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-08 00:00:00.000000000 Z
11
+ date: 2026-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -71,6 +71,7 @@ files:
71
71
  - bin/h-app
72
72
  - bin/h-branch
73
73
  - bin/h-buffer
74
+ - bin/h-commit
74
75
  - bin/h-config
75
76
  - bin/h-home
76
77
  - bin/h-html