open-ready 0.1.2 → 0.1.6

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.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # open-ready
2
+
3
+ Smart dev server launcher that watches your server's output and automatically opens the browser when ready — or opens an AI assistant with the error context when something goes wrong.
4
+
5
+ Works with Next.js, Vite, and any CLI-based dev server.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm install -g open-ready
11
+ # or use without installing:
12
+ npx open-ready <your-dev-command>
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```sh
18
+ open-ready <command> [options]
19
+ ```
20
+
21
+ ### Examples
22
+
23
+ ```sh
24
+ open-ready npm run dev
25
+ open-ready bun run dev
26
+ open-ready vite
27
+ open-ready next dev
28
+ ```
29
+
30
+ ### Options
31
+
32
+ | Flag | Default | Description |
33
+ |------|---------|-------------|
34
+ | `--ai-base <url>` | `https://perplexity.ai?q=` | AI assistant base URL to open on error |
35
+ | `--noAi` | `false` | Disable opening AI on error |
36
+ | `--noOpen` | `false` | Disable opening browser when ready |
37
+ | `--pollDelay <ms>` | `1200` | How often to poll the log for ready/error signals |
38
+
39
+ ### Disable AI on error
40
+
41
+ ```sh
42
+ open-ready npm run dev --noAi
43
+ ```
44
+
45
+ ### Use a different AI assistant
46
+
47
+ ```sh
48
+ open-ready npm run dev --ai-base "https://chatgpt.com/?q="
49
+ ```
50
+
51
+ ## How it works
52
+
53
+ 1. Spawns your dev command and pipes its stdout/stderr to a log file
54
+ 2. Polls the log every `pollDelay` ms looking for:
55
+ - **Error signal** — lines matching `error`, `failed`, `exception`, `SyntaxError`, or `⨯`
56
+ - **Ready signal** — lines matching `ready - started server` or `Ready in Xms`
57
+ 3. On **error**: extracts up to ~1000 chars of surrounding context and opens your AI assistant with a pre-filled prompt explaining the error and asking for a fix
58
+ 4. On **ready**: waits for the port to be reachable, then opens the local URL in your default browser
59
+
60
+ For Next.js projects, the log is written to `.next/port.log`; otherwise `open-when-ready.log` in the current directory.
61
+
62
+ ## License
63
+
64
+ MIT
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
+
3
+ /**
4
+ * @fileoverview CLI tool that spawns a dev server command, monitors its log
5
+ * output for a "ready" signal, and automatically opens the local URL in the
6
+ * browser. If an error is detected first, it opens an AI assistant with the
7
+ * error context for troubleshooting.
8
+ */
9
+
2
10
  import fsPromises from "fs/promises";
3
11
  import fs from "fs";
4
12
  import path from "path";
@@ -20,18 +28,12 @@ const logPath = fs.existsSync(nextDir)
20
28
  ? path.join(".next", "port.log")
21
29
  : "open-when-ready.log";
22
30
 
23
- console.log(`📁 Log: ${logPath} | Poll: ${pollDelay}ms`);
24
31
 
25
32
  /**
26
- * Opens browser when dev server is ready.
27
- *
28
- * ### Features
29
- * - **Automatic**: Detects server start
30
- * - **Versatile**: Supports multiple browsers
31
- *
32
- * ```js
33
- * console.log("Ready!");
34
- * ```
33
+ * Scans log output for error lines and extracts surrounding context
34
+ * as a URL-encoded string suitable for an AI search query.
35
+ * @param {string} log - Raw log output from the dev server
36
+ * @returns {string} URL-encoded error context, or empty string if no error found
35
37
  */
36
38
  function getErrorContext(log) {
37
39
  const lines = log.split(/\n/);
@@ -53,31 +55,31 @@ function getErrorContext(log) {
53
55
  return "";
54
56
  }
55
57
 
58
+ /**
59
+ * Parses log output to find the local dev server URL (e.g. http://localhost:3000).
60
+ * @param {string} log - Raw log output from the dev server
61
+ * @returns {string|null} The localhost URL, or null if not found
62
+ */
56
63
  function extractUrl(log) {
57
64
  // Better regex for Local: line
58
65
  const localMatch = log.match(/Local:\s+(http:\/\/localhost:\d+)/i);
59
- if (localMatch) {
60
- console.log("✅ EXTRACTED URL:", localMatch[1]);
61
- return localMatch[1];
62
- }
66
+ if (localMatch) return localMatch[1];
63
67
 
64
68
  // Fallback port
65
69
  const portMatch = log.match(/localhost:(\d+)/);
66
- if (portMatch) {
67
- const url = `http://localhost:${portMatch[1]}`;
68
- console.log("✅ FALLBACK URL:", url);
69
- return url;
70
- }
70
+ if (portMatch) return `http://localhost:${portMatch[1]}`;
71
71
 
72
- console.log("❌ NO URL FOUND");
73
72
  return null;
74
73
  }
75
74
 
75
+ /**
76
+ * Main entry point. Spawns the dev server command, pipes its output to a log
77
+ * file, and polls the log for ready/error signals to open the browser or AI helper.
78
+ */
76
79
  async function run() {
77
80
  try {
78
81
  await fsPromises.rm(logPath, { force: true });
79
82
  } catch {}
80
- console.log("🚀 Starting", cmdArgs.join(" "));
81
83
 
82
84
  const proc = spawn(cmdArgs.join(" "), [], {
83
85
  stdio: ["ignore", "pipe", "pipe", "ipc"],
@@ -107,14 +109,9 @@ async function run() {
107
109
  if (logChanged && stats.size > 100) {
108
110
  stableCount = 0;
109
111
  lastLogSize = stats.size;
110
- console.log(
111
- `📊 ${stats.size}B CHANGED | LOG:\n${currentLog.slice(0, 500)}...`,
112
- );
113
112
  } else if (stats.size > 100) {
114
113
  stableCount++;
115
- console.log(`📊 ${stats.size}B STABLE (${stableCount}/5)`);
116
114
  if (stableCount >= 5) {
117
- console.log("🛑 Log stable 5 polls - stopping");
118
115
  clearInterval(poll);
119
116
  return;
120
117
  }
@@ -123,7 +120,6 @@ async function run() {
123
120
  // Error first
124
121
  const errorRegex = /⨯|[Ss]yntax[Ee]rror|error|failed|exception/i;
125
122
  if (errorRegex.test(currentLog) && !noAi && !errorOpened) {
126
- console.log("🎯 ERROR!");
127
123
  const err = getErrorContext(currentLog);
128
124
  const customPrompt = "Explain what the error is, how to fix it, then give a shell script with the best recommended solution.";
129
125
  const prompt = encodeURIComponent(customPrompt + " ");
@@ -138,33 +134,21 @@ async function run() {
138
134
  if (isReadyNow && !lastReadySeen && !opened) {
139
135
  opened = true;
140
136
  lastReadySeen = true;
141
- console.log("🎉 READY DETECTED!");
142
137
  const url = extractUrl(currentLog);
143
138
  if (url && !noOpen) {
144
- console.log("🌐 Testing", url);
145
139
  try {
146
140
  await waitOn(url, { timeout: 10000, http: true });
147
- console.log("✅ HTTP READY!");
148
- } catch (e) {
149
- console.log(
150
- "⚠ HTTP not ready, but opening:",
151
- e.message.slice(0, 50),
152
- );
153
- }
154
- console.log("🚀 OPENING BROWSER:", url);
141
+ } catch {}
155
142
  opener(url);
156
143
  }
157
144
  clearInterval(poll);
158
145
  return;
159
146
  }
160
147
  lastReadySeen = isReadyNow;
161
- } catch (e) {
162
- console.log("Poll error:", e.message);
163
- }
148
+ } catch {}
164
149
  }, pollDelay);
165
150
 
166
151
  process.on("SIGINT", () => {
167
- console.log("\n👋 Exiting (dev server alive)");
168
152
  clearInterval(poll);
169
153
  process.exit(0);
170
154
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-ready",
3
- "version": "0.1.2",
3
+ "version": "0.1.6",
4
4
  "description": "Smart dev server launcher: error→AI search, success→auto-open browser. Any CLI tool.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,6 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "minimist": "^1.2.8",
23
- "open": "^10.1.0",
24
23
  "opener": "^1.5.2",
25
24
  "wait-on": "^8.0.1"
26
25
  },