Skip to content
Documentation menu

CLI walkthrough

CLI

Headless commands for tables, JSON, filters, process inspection, and scoped termination.

Use a named subcommand to stay in the CLI. Commands never open the TUI. They print to stdout or stderr and return a stable code.

There are five subcommands on every platform: list, inspect, kill, watch, and why. Only kill ever signals anything. Windows supports read-only inspect and CLI --tree; --group stays Linux/macOS-only. Use whichever binary you like • kick is shorter.

Listing ports

kick list
$ kick listPROTO  ADDRESS    PORT  PID    PROCESS   STATE   LABELTCP    127.0.0.1  3000  18422  node      LISTEN  web devTCP    127.0.0.1  5173  18510  vite      LISTEN  -TCP    0.0.0.0    5432  1201   postgres  LISTEN  postgres

The table is plain, whitespace-separated text with no box-drawing • so it stays friendly to grep and awk. Withheld metadata shows as -. The LABEL column appears once you’ve configured endpoint labels.

Below the table, notes on stderr explain what the table can’t. A - in the PID column gets a reason: the socket belongs to another user whose processes you can’t read (named by account on Linux, for example root (uid 0)), its owner wasn’t attributed, or no readable process holds it at all, which is normal for kernel-held sockets. On Linux, processes whose executable was deleted or replaced after they started are listed with the original path, a common sign of a stale build or a leftover from a package upgrade. Stdout stays a clean table.

kick list
$ kick listPROTO  ADDRESS    PORT  PID    PROCESS  STATETCP    127.0.0.1  3000  18422  devsrv   LISTENTCP    127.0.0.1  5432  -      -        LISTENnote: PID "-" means the socket's owner is not visible:  1 row(s) belong to other users (postgres (uid 970): 1); at least 412 process(es) could not be read (permission denied). Rerun with sudo to see their owners.note: 1 listed process(es) run an executable that was deleted or replaced after they started (often a stale build or an upgraded package):  PID 18422 (devsrv): /home/me/app/target/debug/devsrv

Human-readable endpoints retain IPv6 interface scope, for example [fe80::1%3]:5353, throughout the list table, TUI, inspect, watch, Why, search, diagnostics, and kill confirmations. %unavailable means the native collector could not report an interface index, which is expected for IPv6 observations on Linux and macOS; it is not part of the literal address or an error. Legacy list --json remains unchanged; versioned structured output carries scope separately.

Narrowing the list

list options
kick list --port 3000
kick list --process node
kick list --filter scope:public
kick list --filter 'label:web family:ipv4'
kick list --sort pid
  • --port PORT • only the rows bound to one exact port.
  • --process TEXT • rows whose process name contains the text.
  • --filter TEXT • TUI-style search text or structured filters (see filters).
  • --sort MODE • port, pid, protocol, process, parent, or scope.

JSON for scripts

list --json prints a stable, documented shape • the field names and enum casing are a contract, so it’s safe to parse.

kick list --port 3000 --json
$ kick list --port 3000 --json
[
  {
    "protocol": "tcp",
    "local_addr": "127.0.0.1",
    "local_port": 3000,
    "state": "listen",
    "pid": 18422,
    "process_name": "node",
    "executable_path": "/usr/bin/node",
    "command_line": "node server.js",
    "parent_pid": 18001,
    "parent_process_name": "cursor-agent",
    "child_pids": [],
    "protected": false,
    "platform": "linux",
    "permission": "full",
    "label": "web dev"
  }
]

Missing metadata is null, never an empty string. An empty result is always [].

For the complete in-scope observation • every native TCP state, owner identity, completeness, and evidence gap • use --snapshot-json instead:

full snapshot
kick list --snapshot-json

Snapshot mode deliberately ignores --port, --process, --filter, --sort, and hide_system_processes, and omits full command lines. See Structured output.

Killing by port or PID

kill takes ports or PIDs • one target, or several at once • and asks before it does anything.

kick kill --pid 18422
$ kick kill --pid 18422Terminate PID 18422 (node)Ports: TCP 127.0.0.1:3000Command: kill 18422Type y to confirm, or press Enter to cancel: ysent SIGTERM to PID 18422 (node)PID 18422 (node) exitedconfirmed target ports are no longer visible

A delivered signal is not an exit, and a closed listener isn’t one either. After delivery, kill waits up to two seconds and reports both facts on their own lines. A server that closes its listener but keeps running gets a warning that it’s still running, with the exact command that sends SIGKILL. While the process still owns its port that’s kick kill --pid PID --force. Once the port is closed, a plain --pid kill no longer applies, so the hint is kick kill --pid PID --tree --force, which also stops the process’s children. The exit code still means the signal was accepted.

On Linux, an unprivileged kill --port usually can’t prove the visible owner is the port’s only holder, because other users’ processes can’t be read and one of them could share the socket. It’s refused before any prompt, and the error names the owner and the kick kill --pid PID command that works.

kill examples
kick kill --port 3000
kick kill --pid 18422
kick kill --port 3000 --yes
kick kill --pid 18422 --force
  • --force • stronger force confirmation; Unix uses kill -9, while Windows delivery is hard termination either way.
  • --yes • skip the normal prompt for a target you already trust.

Several targets at once

Repeat --pid or --port, or separate values with commas, to clean up to 128 processes in one command. Every target is resolved first; if any of them can’t be resolved, is protected, or can’t be proven to own its port, Kickoutchi lists the refusals and sends nothing. Otherwise you get one banner, one prompt, and one summary line. Only failures, survivors, and ports that stayed visible get their own lines.

kick kill --pid 18422,18510,18611 --yes
$ kick kill --pid 18422,18510,18611 --yesTerminate 3 processes:  PID 18422 (node): TCP 127.0.0.1:3000  PID 18510 (vite): TCP 127.0.0.1:5173  PID 18611 (node): TCP 127.0.0.1:4000summary: sent SIGTERM to 3 of 3 process(es); 3 exited; 3 of 3 confirmed port(s) no longer visible; 0 failed

Each target keeps every single-process check. The exit code is the most serious outcome across targets. --tree and --group still take one target.

On Linux and macOS, Ctrl-C during a kill waits for frozen-process cleanup. If delivery has not started, Kickoutchi aborts and resumes only processes it stopped; processes that were already stopped stay stopped. Once delivery starts, the bounded delivery and cleanup sequence finishes before the interrupt takes effect. This applies to single-process, tree, and group kills, and also handles SIGTERM and SIGHUP sent to the CLI.

Inspecting a process family

inspect is the read-only way to choose the right root before a scoped kill. It shows ancestors, descendants, siblings, command lines, ports, and suggested tree commands. Linux/macOS also show process-group members and --group hints.

inspect examples
kick inspect --port 3000
kick inspect --pid 18422

Tree and group scope

Normal kill still targets one PID. --tree adds descendant cleanup on Linux, macOS, and Windows; --group is the Linux/macOS-only POSIX process-group scope:

scoped kill examples
kick kill --port 3000 --tree
kick kill --pid 18422 --tree
kick kill --pid 18422 --group
  • --tree • terminate the target and its descendants. Type tree to confirm, or force with --force.
  • --group • terminate every visible member of the target’s POSIX process group. Type group to confirm, or force with --force.
  • --tree and --group conflict; pick one scope.
  • Windows rejects --group and does not expose TUI t/T tree keys. Windows CLI --tree uses Job Object containment and hard termination.

See Scoped kills for the Unix freeze-first and Windows Job Object safety details.

Watching and explaining

Two read-only commands answer questions list can’t:

watch and why
kick why 3000
kick why 3000 --all-protocols --all-addresses --json
kick watch --tcp --port 3000
kick watch --filter state:listen --duration 30s --json
  • why PORT • takes one snapshot, then attempts the exact bind and closes it immediately. It tells occupied, permission denied, address unavailable, and unsupported apart, with the evidence behind each verdict.
  • watch • polls full-state snapshots and streams baseline, bind, release, replacement, and collection_gap events, as terminal output or NDJSON.

Neither one signals a process or runs Docker. Full walkthrough: Watch & Why.

How the CLI behaves

  • An empty unfiltered list is success (exit 0) • a quiet machine isn’t an error.
  • An empty filtered result is no-match (exit 3) • so kick list --port 3000 && echo busy works as an occupancy probe.
  • kill --port refuses ambiguous ports • if two PIDs own one port, it lists the candidates and asks you to pick with --pid instead of guessing.
  • A port with no readable owner exits 4 (permission denied) rather than pretending it can act, and says why, for example that the socket belongs to another user.

When a --port lookup finds no socket but a process mentions that port on its command line, Kickoutchi prints a “possible related process” hint to stderr so JSON on stdout stays clean.

Next

Want the terse index of every flag, filter, and exit code? See the command reference. Parsing the output? See Structured output.