devprobe 0.1.0 → 0.2.0

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.
Files changed (3) hide show
  1. package/README.md +68 -88
  2. package/dist/index.js +77 -20
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,126 +1,106 @@
1
- # portprobe
1
+ # devprobe
2
2
 
3
- Instant port diagnostics for your local dev environment.
3
+ **Instant diagnostics for your local dev environment.** One command — see every listening port, which process owns it, and whether it responds.
4
4
 
5
- ## Why
6
-
7
- Every developer has hit it: you start a server and get `EADDRINUSE`, then spend minutes figuring out what's hogging the port. AI coding agents like Claude Code hit the same wall -- they burn tokens retrying commands and guessing what went wrong.
8
-
9
- portprobe scans common dev ports in parallel, shows what's listening, which process owns it, and whether it responds to HTTP. One command, zero guesswork.
10
-
11
- ## Quick start
12
-
13
- Run without installing:
5
+ [![npm](https://img.shields.io/npm/v/devprobe)](https://www.npmjs.com/package/devprobe)
6
+ [![license](https://img.shields.io/npm/l/devprobe)](LICENSE)
14
7
 
15
- ```bash
16
- npx portprobe
17
- ```
18
-
19
- Or install globally:
8
+ ---
20
9
 
21
10
  ```bash
22
- npm i -g portprobe
11
+ $ npx devprobe
12
+
13
+ PORT | PID | PROCESS | STATUS | LATENCY | PROTO
14
+ -------|-------|------------------|--------|---------|--------
15
+ 3000 | 12841 | node | UP | 2ms | HTTP 200
16
+ 5432 | 1023 | postgres | UP | 1ms | TCP
17
+ 5500 | 9912 | node | UP | 1ms | HTTP 200
18
+ 6379 | 1087 | redis-server | UP | <1ms | TCP
19
+ 8080 | 5523 | java | UP | 12ms | HTTP 200
20
+ 24282 | 3301 | python | UP | 3ms | HTTP 404
23
21
  ```
24
22
 
25
- ## Usage
26
-
27
- ### Default scan
28
-
29
- Scans common dev ports (3000, 3001, 4200, 5000, 5173, 5432, 6379, 8000, 8080, 8443, 9000, 27017) and prints a table:
30
-
31
- ```bash
32
- portprobe
33
- ```
34
-
35
- ### JSON output
36
-
37
- Machine-readable output for scripts and AI agents:
38
-
39
- ```bash
40
- portprobe --json
41
- ```
23
+ No config. No port lists to maintain. It asks the OS directly what's listening.
42
24
 
43
- ### Single port check
44
-
45
- Check whether a specific port is in use:
46
-
47
- ```bash
48
- portprobe 3000
49
- ```
25
+ ## Why
50
26
 
51
- ### Range scan
27
+ - `EADDRINUSE` — what's using port 3000? Now you know instantly
28
+ - **AI agents** (Claude Code, Cursor, etc.) waste tokens running `lsof`, parsing output, retrying — `devprobe --json` gives them structured data in one call
29
+ - **Zero guesswork** — shows ALL listening ports, not just a predefined list
52
30
 
53
- Scan a custom range of ports:
31
+ ## Install
54
32
 
55
33
  ```bash
56
- portprobe --range 3000-9000
34
+ npx devprobe # run without installing
35
+ npm i -g devprobe # or install globally
57
36
  ```
58
37
 
59
- ### Custom timeout
60
-
61
- Set the TCP connection timeout in milliseconds (default: 1000):
38
+ ## Usage
62
39
 
63
40
  ```bash
64
- portprobe --timeout 2000
41
+ devprobe # show all listening ports
42
+ devprobe 3000 # check specific port
43
+ devprobe --range 3000-9000 # scan a range
44
+ devprobe --json # JSON output (for scripts & AI agents)
45
+ devprobe --timeout 2000 # custom timeout in ms
65
46
  ```
66
47
 
67
- ## Flags
48
+ ### Flags
68
49
 
69
50
  | Flag | Description | Default |
70
51
  |---|---|---|
71
52
  | `[port]` | Check a single port | — |
72
53
  | `--json` | Output as JSON | `false` |
73
- | `--range <from-to>` | Scan a port range (e.g. `3000-9000`) | — |
54
+ | `--range <from-to>` | Scan a port range | — |
74
55
  | `--timeout <ms>` | Connection timeout in ms | `1000` |
75
56
  | `--version` | Show version | — |
76
57
  | `--help` | Show help | — |
77
58
 
78
- ## AI Agent integration
79
-
80
- portprobe is designed to work well with AI coding agents. Use `--json` to get structured output that an agent can parse directly:
81
-
82
- ```bash
83
- portprobe --json
84
- ```
59
+ ## For AI Agents
85
60
 
86
- Returns an array of results:
61
+ `devprobe --json` returns structured data that any AI coding agent can parse:
87
62
 
88
63
  ```json
89
- [
90
- {
91
- "port": 3000,
92
- "status": "up",
93
- "pid": 12345,
94
- "process": "node",
95
- "latency": 2,
96
- "protocol": "http"
97
- },
98
- {
99
- "port": 5432,
100
- "status": "up",
101
- "pid": 501,
102
- "process": "postgres",
103
- "latency": 1,
104
- "protocol": null
105
- },
106
- {
107
- "port": 8080,
108
- "status": "free",
109
- "pid": null,
110
- "process": null,
111
- "latency": null,
112
- "protocol": null
113
- }
114
- ]
64
+ {
65
+ "ports": [
66
+ {
67
+ "port": 3000,
68
+ "status": "up",
69
+ "pid": 12841,
70
+ "process": "node",
71
+ "latency": 2,
72
+ "protocol": "HTTP 200"
73
+ }
74
+ ]
75
+ }
115
76
  ```
116
77
 
117
- An agent can run `npx portprobe --json` before starting a dev server to pick an open port or diagnose a conflict without trial and error.
78
+ Add to your project's `CLAUDE.md` or agent config:
79
+ ```
80
+ Use `devprobe --json` to diagnose port conflicts instead of lsof/netstat.
81
+ ```
118
82
 
119
83
  ## Platforms
120
84
 
121
- - **macOS** — full support (uses `lsof` for process resolution)
122
- - **Linux** — full support (uses `lsof` for process resolution)
123
- - **Windows** — best-effort (uses `netstat` for process resolution)
85
+ - **macOS** — full support (`lsof`)
86
+ - **Linux** — full support (`lsof`)
87
+ - **Windows** — best-effort (`netstat`)
88
+
89
+ ## Roadmap
90
+
91
+ - [ ] `--watch` — realtime monitoring
92
+ - [ ] `--wait <port>` — wait until port is available
93
+ - [ ] `--diagnose` — problem analysis + recommendations
94
+ - [ ] Auto-detect service type (PostgreSQL, Redis, MySQL, etc.)
95
+ - [ ] Docker container awareness
96
+
97
+ ## Support the project
98
+
99
+ If devprobe saved you time, consider:
100
+
101
+ - Star this repo — it helps others find it
102
+ - [Sponsor on GitHub](https://github.com/sponsors/bogdanblare) — support development directly
103
+ - Share with your team — the more users, the better it gets
124
104
 
125
105
  ## License
126
106
 
package/dist/index.js CHANGED
@@ -33,6 +33,65 @@ function checkTcp(port, timeout) {
33
33
  import { execFile } from "child_process";
34
34
  import { promisify } from "util";
35
35
  var execFileAsync = promisify(execFile);
36
+ async function discoverListeningPorts() {
37
+ try {
38
+ if (process.platform === "win32") {
39
+ return discoverWindows();
40
+ }
41
+ return discoverUnix();
42
+ } catch {
43
+ return [];
44
+ }
45
+ }
46
+ async function discoverUnix() {
47
+ const { stdout } = await execFileAsync("lsof", [
48
+ "-iTCP",
49
+ "-sTCP:LISTEN",
50
+ "-P",
51
+ "-n",
52
+ "-Fpn"
53
+ ]);
54
+ const results = [];
55
+ let currentPid = null;
56
+ for (const line of stdout.split("\n")) {
57
+ if (line.startsWith("p")) {
58
+ currentPid = parseInt(line.slice(1), 10);
59
+ } else if (line.startsWith("n") && currentPid !== null) {
60
+ const match = line.match(/:(\d+)$/);
61
+ if (match) {
62
+ const port = parseInt(match[1], 10);
63
+ const name = await getProcessName(currentPid);
64
+ results.push({ port, pid: currentPid, name });
65
+ }
66
+ }
67
+ }
68
+ const seen = /* @__PURE__ */ new Set();
69
+ return results.filter((r) => {
70
+ if (seen.has(r.port)) return false;
71
+ seen.add(r.port);
72
+ return true;
73
+ });
74
+ }
75
+ async function discoverWindows() {
76
+ const { stdout } = await execFileAsync("netstat", ["-ano"]);
77
+ const results = [];
78
+ const seen = /* @__PURE__ */ new Set();
79
+ for (const line of stdout.split("\n")) {
80
+ if (!line.includes("LISTENING")) continue;
81
+ const parts = line.trim().split(/\s+/);
82
+ const addrPart = parts[1];
83
+ if (!addrPart) continue;
84
+ const match = addrPart.match(/:(\d+)$/);
85
+ if (!match) continue;
86
+ const port = parseInt(match[1], 10);
87
+ if (seen.has(port)) continue;
88
+ seen.add(port);
89
+ const pid = parseInt(parts[parts.length - 1], 10);
90
+ const name = await getProcessName(pid);
91
+ results.push({ port, pid, name });
92
+ }
93
+ return results;
94
+ }
36
95
  async function resolveProcess(port) {
37
96
  try {
38
97
  if (process.platform === "win32") {
@@ -121,30 +180,30 @@ function checkHttp(port, timeout) {
121
180
  }
122
181
 
123
182
  // src/constants.ts
124
- var DEFAULT_PORTS = [
125
- 3e3,
126
- 3001,
127
- 4200,
128
- 5e3,
129
- 5173,
130
- 5432,
131
- 6379,
132
- 8e3,
133
- 8080,
134
- 8443,
135
- 9e3,
136
- 27017
137
- ];
138
183
  var DEFAULT_TIMEOUT = 1e3;
139
184
 
140
185
  // src/scanner/index.ts
141
186
  async function scanPorts(options) {
142
187
  const timeout = options.timeout ?? DEFAULT_TIMEOUT;
143
- const ports = resolvePorts(options);
188
+ if (options.ports && options.ports.length > 0 || options.range) {
189
+ const ports = resolvePorts(options);
190
+ return Promise.all(ports.map((port) => scanSinglePort(port, timeout)));
191
+ }
192
+ const listening = await discoverListeningPorts();
144
193
  const results = await Promise.all(
145
- ports.map((port) => scanSinglePort(port, timeout))
194
+ listening.map(async (entry) => {
195
+ const httpResult = await checkHttp(entry.port, timeout);
196
+ return {
197
+ port: entry.port,
198
+ status: "up",
199
+ pid: entry.pid,
200
+ process: entry.name,
201
+ latency: httpResult.latency,
202
+ protocol: httpResult.protocol ?? "TCP"
203
+ };
204
+ })
146
205
  );
147
- return results;
206
+ return results.sort((a, b) => a.port - b.port);
148
207
  }
149
208
  function resolvePorts(options) {
150
209
  if (options.ports && options.ports.length > 0) return options.ports;
@@ -205,7 +264,7 @@ function formatJson(results) {
205
264
  }
206
265
 
207
266
  // src/index.ts
208
- program.name("portprobe").description("Instant diagnostics for your local dev environment").version("0.1.0").argument("[port]", "specific port to check", parseInt).option("--json", "output as JSON (for AI agents)").option("--range <from-to>", "scan port range (e.g. 3000-9000)").option("--timeout <ms>", "connection timeout in ms", parseInt).action(async (port, options) => {
267
+ program.name("devprobe").description("Instant diagnostics for your local dev environment").version("0.1.0").argument("[port]", "specific port to check", parseInt).option("--json", "output as JSON (for AI agents)").option("--range <from-to>", "scan port range (e.g. 3000-9000)").option("--timeout <ms>", "connection timeout in ms", parseInt).action(async (port, options) => {
209
268
  const timeout = options.timeout ?? DEFAULT_TIMEOUT;
210
269
  let ports;
211
270
  let range;
@@ -214,8 +273,6 @@ program.name("portprobe").description("Instant diagnostics for your local dev en
214
273
  } else if (options.range) {
215
274
  const [from, to] = options.range.split("-").map(Number);
216
275
  range = { from, to };
217
- } else {
218
- ports = DEFAULT_PORTS;
219
276
  }
220
277
  const results = await scanPorts({ ports, range, timeout });
221
278
  if (options.json) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devprobe",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Instant diagnostics for your local dev environment",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,9 +31,9 @@
31
31
  ],
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "git+https://github.com/bogdanblare/port.probe.git"
34
+ "url": "git+https://github.com/bogdanblare/devprobe.git"
35
35
  },
36
- "homepage": "https://github.com/bogdanblare/port.probe#readme",
36
+ "homepage": "https://github.com/bogdanblare/devprobe#readme",
37
37
  "dependencies": {
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3"