hiiro 0.1.343 → 0.1.345

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: e0b4a52f12f6b8206e79bc9c467c11523d8b6c09592e83ee88617133112ee60a
4
- data.tar.gz: 3190f9942e4f705fb7b4de299ed992d0b0123e5d57c5e375a3476f0b018b4867
3
+ metadata.gz: e46cf30e0170d883a3b5dcf6e74e6cd922b344c71f58dd45d0b7d2ec05eefa41
4
+ data.tar.gz: 05db55a107104823401ce9b3a07a2437943d8250a5bdced2d0bfaa87c53cbd15
5
5
  SHA512:
6
- metadata.gz: a6ce0e2c104da7509260220b5fc02162dde679457bc56012b704b17cb02ede55e51cd0e47f06b626ac40fd129b8f84991c0043e19523513e7012e11b4d8942b8
7
- data.tar.gz: c1b053c3f8cac1f4af5433a64e217dfdc79c93dcf017eab287efbfeb1f2e90ea696012bb37da1a599aab43c069e309b6488c3cab530cdefbd7cfc0cf6c69dde7
6
+ metadata.gz: 576c57204f0e860d74068168bc8483af0a495e1afe7d562bb482142ea1af190c13c3958712978ffa1d0e36a5fdacca4c3bec201c9adbacec4918ba46679de2cd
7
+ data.tar.gz: 97a7acaf188780eef1ad9ca522e43111c19fde4ea17f1738fc7d54465617433a887c0b73b1390f481cd1606d41f9cac6c58dd42444afd56c66730206bd99fb00
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.345] - 2026-04-12
4
+
5
+ ### Fixed
6
+ - ANSI escape sequence pattern now uses hex escapes (`\x20-\x2f`) instead of literal space-to-slash to avoid ambiguity with the `/` regex delimiter
7
+
3
8
  ## [0.1.343] - 2026-04-12
4
9
 
5
10
  ### Added
data/lib/hiiro/shell.rb CHANGED
@@ -22,5 +22,120 @@ class Hiiro
22
22
 
23
23
  selected&.chomp
24
24
  end
25
+
26
+ def self.run(*command, **env)
27
+ env = env.transform_keys(&:to_s).transform_values(&:to_s)
28
+ stdout, status = Open3.capture2(env, *command)
29
+ Result.new(stdout, status)
30
+ end
31
+
32
+ def self.run_combined(*command, **env)
33
+ env = env.transform_keys(&:to_s).transform_values(&:to_s)
34
+ output, status = Open3.capture2e(env, *command)
35
+ Result.new(output, status)
36
+ end
37
+
38
+ def self.run3(*command, **env)
39
+ env = env.transform_keys(&:to_s).transform_values(&:to_s)
40
+ stdout, stderr, status = Open3.capture3(env, *command)
41
+ Result.new(stdout, status, stderr: stderr)
42
+ end
43
+
44
+ # Stream stdout live to $stdout as chunks arrive, buffering for Result.
45
+ # stderr passes through to the parent process (not captured).
46
+ #
47
+ # ~/bin/h-tds does this manually with popen2e for Chromatic:
48
+ #
49
+ # Open3.popen2e('unbuffer pnpm chromatic') do |stdin, stdout_err, wait_thr|
50
+ # stdin.close
51
+ # loop do
52
+ # chunk = stdout_err.readpartial(4096)
53
+ # $stdout.write(chunk)
54
+ # @output << chunk
55
+ # rescue EOFError
56
+ # break
57
+ # end
58
+ # @exit_status = wait_thr.value
59
+ # end
60
+ #
61
+ # stream_combined replaces that pattern — stderr merged in, one Result back.
62
+ def self.stream(*command, tee: $stdout, **env)
63
+ env = env.transform_keys(&:to_s).transform_values(&:to_s)
64
+ Open3.popen2(env, *command) do |stdin, stdout, wait_thr|
65
+ stdin.close
66
+ return Result.collect_chunks(stdout, wait_thr, tee: tee)
67
+ end
68
+ end
69
+
70
+ def self.stream_combined(*command, tee: $stdout, **env)
71
+ env = env.transform_keys(&:to_s).transform_values(&:to_s)
72
+ Open3.popen2e(env, *command) do |stdin, stdout_err, wait_thr|
73
+ stdin.close
74
+ return Result.collect_chunks(stdout_err, wait_thr, tee: tee)
75
+ end
76
+ end
77
+ end
78
+
79
+ class Result
80
+ # Matches the full ANSI/VT100 escape sequence spec:
81
+ # cursor movement, erase, colors, SGR — everything a terminal interprets.
82
+ # [^[] catches all single-char Fe sequences (e.g. \eM, \eD); \[...] catches CSI.
83
+ # \x20-\x2f used instead of space-to-slash to avoid ambiguity with the / regex delimiter.
84
+ ANSI_PATTERN = /\e(?:\[[0-?]*[\x20-\x2f]*[@-~]|[^\[])/
85
+
86
+ attr_reader :stdout, :stderr, :status
87
+
88
+ def initialize(stdout, status, stderr: nil)
89
+ @stdout = stdout
90
+ @stderr = stderr
91
+ @status = status
92
+ end
93
+
94
+ def success?
95
+ status.success?
96
+ end
97
+
98
+ def failed?
99
+ !success?
100
+ end
101
+
102
+ def exit_code
103
+ status.exitstatus
104
+ end
105
+
106
+ # Replay buffered output to the terminal with ANSI sequences intact —
107
+ # colors, cursor movement, line clears all work as they did live.
108
+ # Returns self so you can chain: result.display.lines
109
+ def display(out: $stdout)
110
+ out.write(stdout)
111
+ out.flush
112
+ self
113
+ end
114
+
115
+ # Strip ANSI escape codes for text processing or filtering.
116
+ # Also strips bare \r (carriage-return-only, used by progress bars).
117
+ def plain_text
118
+ stdout.gsub(ANSI_PATTERN, '').gsub(/\r(?!\n)/, '')
119
+ end
120
+
121
+ # Plain text split into non-empty lines.
122
+ def lines
123
+ plain_text.split("\n").reject(&:empty?)
124
+ end
125
+
126
+ # Factory: reads chunks from an IO (popen handle), optionally tee-ing
127
+ # each chunk to `tee` as it arrives. Used by Shell.stream / stream_combined.
128
+ # Pass tee: nil to capture without printing.
129
+ def self.collect_chunks(io, wait_thr, tee: $stdout)
130
+ output = +""
131
+ loop do
132
+ chunk = io.readpartial(4096)
133
+ tee&.write(chunk)
134
+ output << chunk
135
+ rescue EOFError
136
+ break
137
+ end
138
+ new(output, wait_thr.value)
139
+ end
25
140
  end
26
141
  end
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.343"
2
+ VERSION = "0.1.345"
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.343
4
+ version: 0.1.345
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota