hiiro 0.1.334 → 0.1.335
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/CHANGELOG.md +15 -1
- data/README.md +8 -0
- data/bin/h-ps +110 -41
- data/lib/hiiro/ps_process.rb +119 -0
- data/lib/hiiro/version.rb +1 -1
- data/lib/hiiro.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38d14de5a6bc8cbd862309a844bf9637e98e1fe450c85589f4e8c620ff7f65d0
|
|
4
|
+
data.tar.gz: 4de0bc0fe83210c727c275bb05266e27b37b039f4f8c130cb7aac63c471b89c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31f62ef65b741598265dcdbdc08ae95a4a0cc0043c435cebfee9d5a5dd21b98ca580e32875bbbf45f1371aad700f9e6b0891392eaada22cc1a38361236f0d8ce
|
|
7
|
+
data.tar.gz: 3e07dff0a8880ab7368cbdcb39df9e461b3d1e0433c1952d979714b19acb1672686c5e17e240e981a14af419aa94ea755cc93823c8a30a82556c3b60e05270ce
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.335] - 2026-04-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- New `Hiiro::PsProcess` class (`lib/hiiro/ps_process.rb`) for encapsulated process info:
|
|
7
|
+
- `PsProcess.from_line(line)` - parse `ps awwux` output
|
|
8
|
+
- `PsProcess.all`, `.search(pattern)`, `.find(pid)`, `.in_dirs(*paths)`
|
|
9
|
+
- Instance methods: `#files`, `#ports`, `#dir`, `#parent`, `#children`
|
|
10
|
+
- Simple `#to_s` output: PID + CMD
|
|
11
|
+
- New `h ps` subcommands: `info`, `files`, `ports`
|
|
12
|
+
- Smart argument resolution in `h ps`: accepts PID, search pattern, or directory path
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Refactored `h-ps` to use `PsProcess` class instead of raw `ps` parsing
|
|
16
|
+
|
|
3
17
|
## [0.1.334] - 2026-04-07
|
|
4
18
|
|
|
5
19
|
### Added
|
|
@@ -356,4 +370,4 @@
|
|
|
356
370
|
## [0.1.295]
|
|
357
371
|
|
|
358
372
|
### Changed
|
|
359
|
-
- Filter logic changes for PR management
|
|
373
|
+
- Filter logic changes for PR management
|
data/README.md
CHANGED
data/bin/h-ps
CHANGED
|
@@ -3,19 +3,33 @@
|
|
|
3
3
|
require "hiiro"
|
|
4
4
|
|
|
5
5
|
Hiiro.run(*ARGV) do
|
|
6
|
+
# Resolve argument to list of PsProcess instances
|
|
7
|
+
# - all digits: find by PID
|
|
8
|
+
# - valid path: processes with files open in that dir
|
|
9
|
+
# - otherwise: search by pattern
|
|
10
|
+
def resolve_processes(arg)
|
|
11
|
+
return [] if arg.nil?
|
|
12
|
+
|
|
13
|
+
if arg.match?(/^\d+$/)
|
|
14
|
+
p = Hiiro::PsProcess.find(arg)
|
|
15
|
+
p ? [p] : []
|
|
16
|
+
elsif File.directory?(File.expand_path(arg))
|
|
17
|
+
Hiiro::PsProcess.in_dirs(arg)
|
|
18
|
+
else
|
|
19
|
+
Hiiro::PsProcess.search(arg)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
6
23
|
add_subcmd(:search) { |pattern = nil|
|
|
7
24
|
if pattern.nil?
|
|
8
25
|
puts "Usage: h ps search <pattern>"
|
|
9
26
|
next
|
|
10
27
|
end
|
|
11
28
|
|
|
12
|
-
|
|
13
|
-
header = output.first
|
|
14
|
-
matches = output[1..].select { |line| line.include?(pattern) }
|
|
29
|
+
processes = Hiiro::PsProcess.search(pattern)
|
|
15
30
|
|
|
16
|
-
if
|
|
17
|
-
puts
|
|
18
|
-
matches.each { |line| puts line }
|
|
31
|
+
if processes.any?
|
|
32
|
+
processes.each { |p| puts p }
|
|
19
33
|
else
|
|
20
34
|
puts "No processes found matching '#{pattern}'"
|
|
21
35
|
end
|
|
@@ -27,64 +41,119 @@ Hiiro.run(*ARGV) do
|
|
|
27
41
|
next
|
|
28
42
|
end
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
processes = Hiiro::PsProcess.in_dirs(*paths)
|
|
45
|
+
|
|
46
|
+
if processes.empty?
|
|
47
|
+
puts "No processes found with files open in: #{paths.join(', ')}"
|
|
48
|
+
else
|
|
49
|
+
processes.each { |p| puts p }
|
|
50
|
+
end
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
add_subcmd(:getdir) { |pattern = nil|
|
|
54
|
+
if pattern.nil?
|
|
55
|
+
puts "Usage: h ps getdir <pattern>"
|
|
56
|
+
next
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
processes = Hiiro::PsProcess.search(pattern)
|
|
60
|
+
|
|
61
|
+
if processes.empty?
|
|
62
|
+
puts "No processes found matching '#{pattern}'"
|
|
63
|
+
next
|
|
64
|
+
end
|
|
35
65
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
found_any = false
|
|
67
|
+
processes.each do |p|
|
|
68
|
+
dir = p.dir
|
|
69
|
+
if dir
|
|
70
|
+
found_any = true
|
|
71
|
+
puts "#{p.pid}\t#{dir}\t#{p.cmd}"
|
|
39
72
|
end
|
|
40
73
|
end
|
|
41
74
|
|
|
42
|
-
|
|
43
|
-
|
|
75
|
+
puts "Could not determine working directories for matching processes" unless found_any
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
add_subcmd(:info) { |arg = nil|
|
|
79
|
+
if arg.nil?
|
|
80
|
+
puts "Usage: h ps info <pid|pattern|path>"
|
|
44
81
|
next
|
|
45
82
|
end
|
|
46
83
|
|
|
47
|
-
|
|
48
|
-
header = ps_output.first
|
|
49
|
-
matches = ps_output[1..].select { |line| pids.include?(line.split[1]) }
|
|
84
|
+
processes = resolve_processes(arg)
|
|
50
85
|
|
|
51
|
-
|
|
52
|
-
|
|
86
|
+
if processes.empty?
|
|
87
|
+
puts "No processes found for '#{arg}'"
|
|
88
|
+
next
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
processes.each_with_index do |p, i|
|
|
92
|
+
puts "---" if i > 0
|
|
93
|
+
puts "PID: #{p.pid}"
|
|
94
|
+
puts "User: #{p.user}"
|
|
95
|
+
puts "CPU: #{p.cpu}%"
|
|
96
|
+
puts "Mem: #{p.mem}%"
|
|
97
|
+
puts "Stat: #{p.stat}"
|
|
98
|
+
puts "Dir: #{p.dir || '(unknown)'}"
|
|
99
|
+
puts "Cmd: #{p.cmd}"
|
|
100
|
+
|
|
101
|
+
parent = p.parent
|
|
102
|
+
puts "Parent: #{parent.pid} #{parent.cmd.slice(0, 60)}" if parent
|
|
103
|
+
|
|
104
|
+
children = p.children
|
|
105
|
+
if children.any?
|
|
106
|
+
puts "Children:"
|
|
107
|
+
children.each { |c| puts " #{c.pid}\t#{c.cmd.slice(0, 60)}" }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
53
110
|
}
|
|
54
111
|
|
|
55
|
-
add_subcmd(:
|
|
56
|
-
if
|
|
57
|
-
puts "Usage: h ps
|
|
112
|
+
add_subcmd(:files) { |arg = nil|
|
|
113
|
+
if arg.nil?
|
|
114
|
+
puts "Usage: h ps files <pid|pattern|path>"
|
|
58
115
|
next
|
|
59
116
|
end
|
|
60
117
|
|
|
61
|
-
|
|
62
|
-
pids = ps_output[1..].select { |line| line.include?(pattern) }.map { |line| line.split[1] }
|
|
118
|
+
processes = resolve_processes(arg)
|
|
63
119
|
|
|
64
|
-
if
|
|
65
|
-
puts "No processes found
|
|
120
|
+
if processes.empty?
|
|
121
|
+
puts "No processes found for '#{arg}'"
|
|
66
122
|
next
|
|
67
123
|
end
|
|
68
124
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
125
|
+
processes.each do |p|
|
|
126
|
+
puts "=== #{p.pid}\t#{p.cmd.slice(0, 60)}" if processes.size > 1
|
|
127
|
+
files = p.files
|
|
128
|
+
if files.empty?
|
|
129
|
+
puts " (no open files)" if processes.size > 1
|
|
130
|
+
else
|
|
131
|
+
files.each { |f| puts "#{f[:fd]}\t#{f[:type]}\t#{f[:name]}" }
|
|
76
132
|
end
|
|
77
133
|
end
|
|
134
|
+
}
|
|
78
135
|
|
|
79
|
-
|
|
80
|
-
|
|
136
|
+
add_subcmd(:ports) { |arg = nil|
|
|
137
|
+
if arg.nil?
|
|
138
|
+
puts "Usage: h ps ports <pid|pattern|path>"
|
|
81
139
|
next
|
|
82
140
|
end
|
|
83
141
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
puts "#{
|
|
142
|
+
processes = resolve_processes(arg)
|
|
143
|
+
|
|
144
|
+
if processes.empty?
|
|
145
|
+
puts "No processes found for '#{arg}'"
|
|
146
|
+
next
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
processes.each do |p|
|
|
150
|
+
puts "=== #{p.pid}\t#{p.cmd.slice(0, 60)}" if processes.size > 1
|
|
151
|
+
ports = p.ports
|
|
152
|
+
if ports.empty?
|
|
153
|
+
puts " (no open ports)" if processes.size > 1
|
|
154
|
+
else
|
|
155
|
+
ports.each { |pt| puts "#{pt[:protocol]}\t#{pt[:name]}" }
|
|
156
|
+
end
|
|
88
157
|
end
|
|
89
158
|
}
|
|
90
159
|
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'shellwords'
|
|
3
|
+
|
|
4
|
+
class Hiiro::PsProcess
|
|
5
|
+
attr_reader :user, :pid, :cpu, :mem, :vsz, :rss, :tty, :stat, :start, :time, :cmd
|
|
6
|
+
|
|
7
|
+
def initialize(user:, pid:, cpu:, mem:, vsz:, rss:, tty:, stat:, start:, time:, cmd:)
|
|
8
|
+
@user = user
|
|
9
|
+
@pid = pid
|
|
10
|
+
@cpu = cpu
|
|
11
|
+
@mem = mem
|
|
12
|
+
@vsz = vsz
|
|
13
|
+
@rss = rss
|
|
14
|
+
@tty = tty
|
|
15
|
+
@stat = stat
|
|
16
|
+
@start = start
|
|
17
|
+
@time = time
|
|
18
|
+
@cmd = cmd
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Parse a line from `ps awwux` output
|
|
22
|
+
# Format: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND...
|
|
23
|
+
def self.from_line(line)
|
|
24
|
+
parts = line.split
|
|
25
|
+
return nil if parts.size < 11
|
|
26
|
+
|
|
27
|
+
new(
|
|
28
|
+
user: parts[0],
|
|
29
|
+
pid: parts[1],
|
|
30
|
+
cpu: parts[2],
|
|
31
|
+
mem: parts[3],
|
|
32
|
+
vsz: parts[4],
|
|
33
|
+
rss: parts[5],
|
|
34
|
+
tty: parts[6],
|
|
35
|
+
stat: parts[7],
|
|
36
|
+
start: parts[8],
|
|
37
|
+
time: parts[9],
|
|
38
|
+
cmd: parts[10..].join(' ')
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Get all processes
|
|
43
|
+
def self.all
|
|
44
|
+
`ps awwux`.lines[1..].filter_map { |line| from_line(line) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Search processes by pattern (matches against full line)
|
|
48
|
+
def self.search(pattern)
|
|
49
|
+
all.select { |p| p.cmd.include?(pattern) || p.user.include?(pattern) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Find process by PID
|
|
53
|
+
def self.find(pid)
|
|
54
|
+
all.find { |p| p.pid == pid.to_s }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Find processes with files open in given directories
|
|
58
|
+
def self.in_dirs(*paths)
|
|
59
|
+
pids = Set.new
|
|
60
|
+
paths.each do |path|
|
|
61
|
+
expanded = File.expand_path(path)
|
|
62
|
+
lsof_output = `lsof +D #{expanded.shellescape} 2>/dev/null`.lines[1..]
|
|
63
|
+
next unless lsof_output
|
|
64
|
+
|
|
65
|
+
lsof_output.each do |line|
|
|
66
|
+
fields = line.split
|
|
67
|
+
pids << fields[1] if fields[1]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
all.select { |p| pids.include?(p.pid) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Open files for this process
|
|
75
|
+
def files
|
|
76
|
+
`lsof -p #{pid} 2>/dev/null`.lines[1..].map do |line|
|
|
77
|
+
parts = line.split
|
|
78
|
+
{ fd: parts[3], type: parts[4], name: parts[8] } if parts.size >= 9
|
|
79
|
+
end.compact
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Open network ports for this process
|
|
83
|
+
def ports
|
|
84
|
+
`lsof -p #{pid} -i 2>/dev/null`.lines[1..].map do |line|
|
|
85
|
+
parts = line.split
|
|
86
|
+
{ protocol: parts[7], name: parts[8] } if parts.size >= 9
|
|
87
|
+
end.compact
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Current working directory
|
|
91
|
+
def dir
|
|
92
|
+
cwd_line = `lsof -p #{pid} 2>/dev/null | grep ' cwd '`.strip
|
|
93
|
+
return nil if cwd_line.empty?
|
|
94
|
+
cwd_line.split.last
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Parent process
|
|
98
|
+
def parent
|
|
99
|
+
ppid = `ps -o ppid= -p #{pid}`.strip
|
|
100
|
+
return nil if ppid.empty?
|
|
101
|
+
self.class.find(ppid)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Child processes
|
|
105
|
+
def children
|
|
106
|
+
child_pids = `pgrep -P #{pid}`.lines.map(&:strip)
|
|
107
|
+
child_pids.filter_map { |cpid| self.class.find(cpid) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Simple display: PID and CMD
|
|
111
|
+
def to_s
|
|
112
|
+
"#{pid}\t#{cmd}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Detailed display
|
|
116
|
+
def inspect
|
|
117
|
+
"#<PsProcess pid=#{pid} user=#{user} stat=#{stat} cmd=#{cmd.slice(0, 40)}...>"
|
|
118
|
+
end
|
|
119
|
+
end
|
data/lib/hiiro/version.rb
CHANGED
data/lib/hiiro.rb
CHANGED
|
@@ -35,6 +35,7 @@ require_relative "hiiro/todo"
|
|
|
35
35
|
require_relative "hiiro/service_manager"
|
|
36
36
|
require_relative "hiiro/runner_tool"
|
|
37
37
|
require_relative "hiiro/app_files"
|
|
38
|
+
require_relative "hiiro/ps_process"
|
|
38
39
|
require_relative "hiiro/rbenv"
|
|
39
40
|
require_relative "hiiro/any_struct"
|
|
40
41
|
require_relative "hiiro/pinned_pr"
|
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.
|
|
4
|
+
version: 0.1.335
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Toyota
|
|
@@ -351,6 +351,7 @@ files:
|
|
|
351
351
|
- lib/hiiro/pinned_pr_manager.rb
|
|
352
352
|
- lib/hiiro/project.rb
|
|
353
353
|
- lib/hiiro/projects.rb
|
|
354
|
+
- lib/hiiro/ps_process.rb
|
|
354
355
|
- lib/hiiro/queue.rb
|
|
355
356
|
- lib/hiiro/rbenv.rb
|
|
356
357
|
- lib/hiiro/registry.rb
|