Skip to main content

proc_cli/
cli.rs

1//! The command-line definition: `Cli` and its subcommands.
2//!
3//! Lives in the library so tests can read the clap definitions
4//! (`Cli::command()`) and check them against what they exercise.
5
6use crate::commands::{
7    ByCommand, ForCommand, FreeCommand, FreezeCommand, InCommand, InfoCommand, KillCommand,
8    ListCommand, OnCommand, OrphansCommand, PortsCommand, StopCommand, StuckCommand, ThawCommand,
9    TreeCommand, UnstickCommand, WaitCommand, WatchCommand, WhyCommand,
10};
11use clap::{Parser, Subcommand};
12use clap_complete::Shell;
13
14const VERSION_INFO: &str = concat!(
15    env!("PROC_VERSION"),
16    "\nhttps://github.com/yazeed/proc-cli",
17    "\nLicense: MIT"
18);
19
20/// Semantic CLI tool for process management. Target by port, PID, name or path.
21#[derive(Parser)]
22#[command(name = "proc")]
23#[command(author, version = VERSION_INFO, about, long_about = None)]
24#[command(propagate_version = true)]
25#[command(
26    after_help = "Targets: :port, PID, or process name. Give several as separate arguments.
27Run 'proc --help' for examples or visit https://github.com/yazeed/proc-cli"
28)]
29#[command(after_long_help = "EXAMPLES:
30
31  Port/Process Lookup:
32    proc on :3000                  What's on port 3000?
33    proc on :3000 :8080            What's on several ports?
34    proc on node                   What ports are the processes named node using?
35
36  Find by File:
37    proc for ./server              Processes running this program (or holding it open)
38    proc for /usr/bin/node         Processes running this executable
39    proc for app.log               What has this file open? (not searched on Windows)
40
41  Filter by Name:
42    proc by node                   Processes named 'node' (the whole name)
43    proc by node --cmd             Anything with 'node' in its name or command line
44    proc by node --in .            Node processes in current directory
45    proc by node --min-cpu 5       Node processes using >5% CPU
46
47  Filter by Directory:
48    proc in .                      Processes in current directory
49    proc in . --by node            Node processes in cwd
50
51  List All:
52    proc list                      All processes
53    proc list --min-cpu 10         Processes using >10% CPU
54
55  Info/Kill/Stop (multi-target):
56    proc info :3000 :8080          Info for several targets
57    proc kill :3000 node           Kill port 3000 and the processes named node
58    proc kill serve --cmd          Kill processes with 'serve' in their command line
59    proc kill 1234 5678 :8080      Kill two PIDs and whatever is on 8080
60    proc stop :3000 :8080          Stop several targets gracefully
61
62  Watch (Real-Time):
63    proc watch                     Watch all processes (alias: proc top)
64    proc watch node                Watch the processes named node
65    proc watch :3000               Watch process on port 3000
66    proc watch :3000 :8080 node    Watch several targets
67    proc watch -n 1 --sort mem     1s refresh, sorted by memory
68    proc watch --in . --by node    Node processes in current directory
69
70  Freeze/Thaw:
71    proc freeze node               Pause the processes named node (SIGSTOP)
72    proc freeze :3000 --yes        Freeze by port, skip confirm
73    proc thaw node                 Resume frozen processes (SIGCONT)
74
75  Free Ports:
76    proc free :3000                Kill process, verify port freed
77    proc free :3000 :8080 --yes    Free several ports
78
79  Diagnostics:
80    proc why :3000                 Why is port 3000 busy? (ancestry)
81    proc orphans                   Find orphaned processes
82    proc orphans --kill            Find and kill orphans
83
84  Wait:
85    proc wait node                 Wait until node processes exit
86    proc wait 1234 5678            Wait until both exit
87    proc wait :3000 --timeout 60   Wait up to 60s for port to free
88    proc wait node -n 10 -q        Check every 10s, quiet mode
89
90  Other:
91    proc ports                     List all listening TCP ports
92    proc tree --min-cpu 5          Process tree filtered by CPU
93    proc stuck                     Find hung processes
94    proc unstick --force           Recover or terminate stuck processes
95    proc stop nginx --signal USR1  Send a different first signal (force-killed after --timeout if it lives)
96
97Targets: :port, PID, or process name. Give several as separate arguments.
98A name matches the process name, the file name of its executable or the file name
99of its first argument, ignoring case (like pkill -x, but case-insensitive):
100'node' does not match 'Claude Helper'. Add --cmd to match any part of the name
101or the full command line instead (like pkill -f). A path is not a name: use
102--cmd, or 'proc for <file>' for an executable. proc doesn't expand wildcards;
103an unquoted node* is expanded by your shell into file names; use --cmd for
104partial matches.
105Comma-separated targets (:3000,:8080) still work but are deprecated and will be
106removed in a future major release; they print a note on stderr (not with --json).
107--in takes the next word as its directory unless that word starts with '-':
108put it after the targets, or write --in=DIR.
109For more information, visit: https://github.com/yazeed/proc-cli")]
110pub struct Cli {
111    /// The subcommand; none prints the orientation text
112    #[command(subcommand)]
113    pub command: Option<Commands>,
114}
115
116/// Every `proc` subcommand.
117#[derive(Subcommand)]
118pub enum Commands {
119    /// Port/process lookup (bidirectional)
120    #[command(visible_alias = ":")]
121    On(OnCommand),
122
123    /// Find processes by file path
124    #[command(visible_alias = "f")]
125    For(ForCommand),
126
127    /// Filter processes by name
128    #[command(visible_alias = "b")]
129    By(ByCommand),
130
131    /// Filter processes by working directory
132    In(InCommand),
133
134    /// List processes
135    #[command(visible_aliases = ["l", "ps"])]
136    List(ListCommand),
137
138    /// Show detailed process information
139    #[command(visible_aliases = ["i", "inspect"])]
140    Info(InfoCommand),
141
142    /// List all listening TCP ports
143    #[command(visible_alias = "p")]
144    Ports(PortsCommand),
145
146    /// Kill process(es) forcefully
147    #[command(visible_alias = "k")]
148    Kill(KillCommand),
149
150    /// Stop process(es) gracefully
151    #[command(visible_alias = "s")]
152    Stop(StopCommand),
153
154    /// Show process tree
155    #[command(visible_alias = "t")]
156    Tree(TreeCommand),
157
158    /// Watch processes in real-time
159    #[command(visible_aliases = ["w", "top"])]
160    Watch(WatchCommand),
161
162    /// Find stuck/hung processes
163    #[command(visible_alias = "x")]
164    Stuck(StuckCommand),
165
166    /// Attempt to recover stuck processes
167    #[command(visible_alias = "u")]
168    Unstick(UnstickCommand),
169
170    /// Freeze (pause) process(es) with SIGSTOP
171    Freeze(FreezeCommand),
172
173    /// Resume frozen process(es) with SIGCONT
174    Thaw(ThawCommand),
175
176    /// Find orphaned processes
177    #[command(visible_alias = "o")]
178    Orphans(OrphansCommand),
179
180    /// Trace why a port is busy or show process ancestry
181    Why(WhyCommand),
182
183    /// Free port(s) by killing process and verifying availability
184    Free(FreeCommand),
185
186    /// Wait for process(es) to exit
187    Wait(WaitCommand),
188
189    /// Generate shell completions
190    #[command(hide = true)]
191    Completions {
192        /// Shell to generate completions for
193        #[arg(value_enum)]
194        shell: Shell,
195    },
196
197    /// Generate man page
198    #[command(hide = true)]
199    Manpage,
200}