nodewise 1.0.1 → 1.0.3

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 CHANGED
@@ -71,6 +71,9 @@ Traditional error messages can be cryptic, long, or stacked with irrelevant inte
71
71
 
72
72
  ## 📋 Example Output
73
73
 
74
+ <img width="1836" height="694" alt="Screenshot 2026-02-15 223424" src="https://github.com/user-attachments/assets/412f60cf-5ab6-4662-8a0a-1f92145f2485" />
75
+
76
+
74
77
  ✦ GEMINI INTELLIGENCE
75
78
  ─────────────────────────────────────────────
76
79
 
package/bin/nodewise.js CHANGED
@@ -26,7 +26,7 @@ function parseArgs() {
26
26
  setup: false,
27
27
  reset: false
28
28
  };
29
-
29
+
30
30
  let scriptPath = null;
31
31
  const scriptArgs = [];
32
32
 
@@ -78,13 +78,14 @@ ${chalk.bold('MODES')}
78
78
  ${chalk.cyan('Normal Detection')} - Pattern-based error detection (offline)
79
79
 
80
80
  ${chalk.bold('CONFIGURATION')}
81
- Settings are saved in ${chalk.yellow('nodewise.config.json')} in your project root.
81
+ Settings are saved in ${chalk.yellow('node_modules/.nodewise.config.json')} (if available)
82
+ or ${chalk.yellow('.nodewise.config.json')} in your project root.
83
+ This ensures your API keys are not accidentally committed to version control.
82
84
 
83
85
  ${chalk.bold('DOCUMENTATION')}
84
86
  https://github.com/yourusername/nodewise
85
87
  `);
86
88
  }
87
-
88
89
  /**
89
90
  * Display version
90
91
  */
package/logo.png CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodewise",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Node.js error explainer with AI-powered clarity",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -22,10 +22,10 @@
22
22
  "author": "Gourab Das",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "inquirer": "^9.2.15",
26
25
  "axios": "^1.6.5",
26
+ "chalk": "^4.1.2",
27
27
  "chokidar": "^3.5.3",
28
- "chalk": "^4.1.2"
28
+ "inquirer": "^9.2.15"
29
29
  },
30
30
  "files": [
31
31
  "bin",
@@ -44,7 +44,6 @@
44
44
  "url": "https://github.com/Gourab2005/nodewise/issues"
45
45
  },
46
46
  "homepage": "https://github.com/Gourab2005/nodewise#readme",
47
- "devDependencies": {},
48
47
  "engines": {
49
48
  "node": ">=18.0.0"
50
49
  }
package/src/config.js CHANGED
@@ -9,13 +9,47 @@ const fs = require('fs');
9
9
  const path = require('path');
10
10
 
11
11
  const CONFIG_FILE = 'nodewise.config.json';
12
+ const HIDDEN_CONFIG_FILE = '.nodewise.config.json';
12
13
 
13
14
  /**
14
15
  * Get the configuration file path
15
- * Looks in current working directory
16
+ * Prioritizes node_modules to avoid accidental git commits
16
17
  */
17
18
  function getConfigPath() {
18
- return path.join(process.cwd(), CONFIG_FILE);
19
+ const cwd = process.cwd();
20
+ const nodeModulesPath = path.join(cwd, 'node_modules');
21
+ const nodeModulesConfig = path.join(nodeModulesPath, HIDDEN_CONFIG_FILE);
22
+ const rootHiddenConfig = path.join(cwd, HIDDEN_CONFIG_FILE);
23
+ const rootLegacyConfig = path.join(cwd, CONFIG_FILE);
24
+
25
+ // 1. Check if config already exists in node_modules
26
+ if (fs.existsSync(nodeModulesConfig)) {
27
+ return nodeModulesConfig;
28
+ }
29
+
30
+ // 2. Check if hidden config exists in root
31
+ if (fs.existsSync(rootHiddenConfig)) {
32
+ return rootHiddenConfig;
33
+ }
34
+
35
+ // 3. Check if legacy config exists in root
36
+ if (fs.existsSync(rootLegacyConfig)) {
37
+ return rootLegacyConfig;
38
+ }
39
+
40
+ // 4. For NEW configs:
41
+ // If node_modules exists, use it (recommended for security)
42
+ if (fs.existsSync(nodeModulesPath)) {
43
+ try {
44
+ // Ensure node_modules exists (redundant but safe)
45
+ return nodeModulesConfig;
46
+ } catch (e) {
47
+ // Fallback to root if node_modules is not writable
48
+ }
49
+ }
50
+
51
+ // Final fallback to hidden root config
52
+ return rootHiddenConfig;
19
53
  }
20
54
 
21
55
  /**
@@ -163,5 +197,6 @@ module.exports = {
163
197
  mergeWithDefaults,
164
198
  createConfig,
165
199
  updateConfig,
166
- CONFIG_FILE
200
+ CONFIG_FILE,
201
+ HIDDEN_CONFIG_FILE
167
202
  };
@@ -8,7 +8,14 @@ const chalk = require('chalk');
8
8
  const readline = require('readline');
9
9
  const { explain } = require('./index');
10
10
 
11
+ // Global state for cancellation
12
+ let cancelPrompt = false;
13
+ let activePromptResolver = null;
14
+
11
15
  async function showInteractiveExplainer(errorText, config) {
16
+ // Reset cancel flag
17
+ cancelPrompt = false;
18
+
12
19
  console.log('\n');
13
20
  const summary = (errorText || '').toString().split('\n')[0] || 'Unknown error';
14
21
 
@@ -20,34 +27,105 @@ async function showInteractiveExplainer(errorText, config) {
20
27
  console.log(chalk.white(' ' + summary));
21
28
  console.log();
22
29
 
23
- // Compact, modern prompt
24
- console.log(chalk.cyan.bold(' ? ') + chalk.white('Would you like an AI explanation?'));
25
- console.log(chalk.gray(' [y] Yes, explain it [n] No, just skip'));
26
- console.log();
27
-
28
30
  const answer = await getUserInput();
29
31
 
30
- if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === '') {
32
+ if (answer === 'yes') {
31
33
  await explainErrorInteractively(errorText, config);
34
+ } else if (answer === 'cancelled') {
35
+ // Silently skip on cancellation
32
36
  } else {
33
37
  console.log(chalk.gray(' Skipped.\n'));
34
38
  }
35
39
  }
36
40
 
37
- function getUserInput() {
41
+ async function getUserInput() {
38
42
  return new Promise((resolve) => {
39
- const rl = readline.createInterface({
40
- input: process.stdin,
41
- output: process.stdout,
42
- terminal: true
43
- });
44
-
45
- process.stdout.write(chalk.cyan(' > '));
46
-
47
- rl.on('line', (answer) => {
48
- rl.close();
49
- resolve(answer.trim());
50
- });
43
+ const options = [
44
+ { label: chalk.green('✓ Yes, explain it'), value: 'yes' },
45
+ { label: chalk.gray('✗ No, just skip'), value: 'no' }
46
+ ];
47
+
48
+ let selectedIndex = 0;
49
+ let isResolved = false;
50
+
51
+ // Store resolver for cancellation
52
+ activePromptResolver = () => {
53
+ if (!isResolved) {
54
+ cleanup();
55
+ isResolved = true;
56
+ resolve('cancelled');
57
+ }
58
+ };
59
+
60
+ // Display the question
61
+ console.log(chalk.cyan(' ? ') + chalk.white('Would you like an AI explanation?'));
62
+
63
+ const renderOptions = () => {
64
+ options.forEach((option, index) => {
65
+ const prefix = index === selectedIndex ? chalk.cyan('❯ ') : ' ';
66
+ console.log(` ${prefix}${option.label}`);
67
+ });
68
+ };
69
+
70
+ const cleanup = () => {
71
+ process.stdin.removeListener('keypress', onKeypress);
72
+ if (process.stdin.isTTY) {
73
+ process.stdin.setRawMode(false);
74
+ }
75
+ process.stdin.pause();
76
+ activePromptResolver = null;
77
+ };
78
+
79
+ // Initial render
80
+ renderOptions();
81
+
82
+ // Setup readline for keypress
83
+ readline.emitKeypressEvents(process.stdin);
84
+ if (process.stdin.isTTY) {
85
+ process.stdin.setRawMode(true);
86
+ }
87
+
88
+ const onKeypress = (str, key) => {
89
+ // Check if cancelled
90
+ if (cancelPrompt) {
91
+ cleanup();
92
+ if (!isResolved) {
93
+ isResolved = true;
94
+ console.log(chalk.yellow('\n (Skipped due to restart)\n'));
95
+ resolve('cancelled');
96
+ }
97
+ return;
98
+ }
99
+
100
+ if (key.name === 'up') {
101
+ // Clear previous options
102
+ process.stdout.write('\x1b[' + options.length + 'A'); // Move cursor up
103
+ process.stdout.write('\x1b[0J'); // Clear from cursor down
104
+
105
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : options.length - 1;
106
+ renderOptions();
107
+ } else if (key.name === 'down') {
108
+ // Clear previous options
109
+ process.stdout.write('\x1b[' + options.length + 'A'); // Move cursor up
110
+ process.stdout.write('\x1b[0J'); // Clear from cursor down
111
+
112
+ selectedIndex = selectedIndex < options.length - 1 ? selectedIndex + 1 : 0;
113
+ renderOptions();
114
+ } else if (key.name === 'return') {
115
+ cleanup();
116
+ if (!isResolved) {
117
+ isResolved = true;
118
+ console.log(); // Add newline after selection
119
+ resolve(options[selectedIndex].value);
120
+ }
121
+ } else if (key.ctrl && key.name === 'c') {
122
+ cleanup();
123
+ process.exit(0);
124
+ }
125
+ };
126
+
127
+ process.stdin.on('keypress', onKeypress);
128
+ process.stdin.resume();
51
129
  });
52
130
  }
53
131
 
@@ -69,6 +147,16 @@ function clearThinking(interval) {
69
147
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
70
148
  }
71
149
 
150
+ /**
151
+ * Cancel any active prompt
152
+ */
153
+ function cancelActivePrompt() {
154
+ cancelPrompt = true;
155
+ if (activePromptResolver) {
156
+ activePromptResolver();
157
+ }
158
+ }
159
+
72
160
  function displayExplanation(explanation) {
73
161
  console.log('\n');
74
162
 
@@ -87,12 +175,12 @@ function displayExplanation(explanation) {
87
175
 
88
176
  // High-end minimalist styling
89
177
  // If it starts with a keyword, add a line break before it to create gaps between sections
90
- if (/^(Summary|Problem|Cause|Solution|Fix|Where|Why|File|Line|Note|Suggestion):/i.test(content)) {
178
+ if (/^(Summary|Problem|Cause|Solution|Fix|Where|Why|File|Line|Note|Suggestion|Minimal code fix|Code):/i.test(content)) {
91
179
  console.log();
92
180
  }
93
181
 
94
182
  content = content
95
- .replace(/^(Summary|Problem|Cause|Solution|Fix|Where|Why|File|Line|Note|Suggestion):/i, (match) => chalk.hex('#FFAF00').bold(match))
183
+ .replace(/^(Summary|Problem|Cause|Solution|Fix|Where|Why|File|Line|Note|Suggestion|Minimal code fix|Code):/i, (match) => chalk.hex('#FFAF00').bold(match))
96
184
  .replace(/`([^`]+)`/g, (match) => chalk.hex('#00FF87')(match))
97
185
  .replace(/'([^']+)'/g, (match) => chalk.hex('#00FF87')(match));
98
186
 
@@ -119,5 +207,6 @@ async function explainErrorInteractively(errorText, config) {
119
207
 
120
208
  module.exports = {
121
209
  showInteractiveExplainer,
122
- displayExplanation
210
+ displayExplanation,
211
+ cancelActivePrompt
123
212
  };
package/src/runner.js CHANGED
@@ -12,7 +12,7 @@ const { spawn } = require('child_process');
12
12
  const chokidar = require('chokidar');
13
13
  const chalk = require('chalk');
14
14
  const { explain } = require('./explainer');
15
- const { showInteractiveExplainer } = require('./explainer/interactive');
15
+ const { showInteractiveExplainer, cancelActivePrompt } = require('./explainer/interactive');
16
16
 
17
17
  class Runner {
18
18
  constructor(scriptPath, args = [], config = {}) {
@@ -57,7 +57,7 @@ class Runner {
57
57
  const text = data.toString();
58
58
  process.stderr.write(text);
59
59
  errorBuffer += text;
60
-
60
+
61
61
  // Detect errors in stderr and explain them immediately
62
62
  if (this.isErrorOutput(text)) {
63
63
  this.explainErrorWithDebounce(text);
@@ -85,7 +85,7 @@ class Runner {
85
85
 
86
86
  this.child.on('exit', (code, signal) => {
87
87
  this.isProcessExiting = true;
88
-
88
+
89
89
  if (code !== 0 && code !== null) {
90
90
  // Process exited with error
91
91
  if (!this.isRestarting && errorBuffer) {
@@ -148,15 +148,18 @@ class Runner {
148
148
 
149
149
  this.isRestarting = true;
150
150
 
151
+ // Cancel any active interactive prompt
152
+ cancelActivePrompt();
153
+
151
154
  if (this.child) {
152
155
  this.child.kill('SIGTERM');
153
-
156
+
154
157
  // Wait a bit for graceful shutdown
155
158
  setTimeout(() => {
156
159
  if (this.child && !this.child.killed) {
157
160
  this.child.kill('SIGKILL');
158
161
  }
159
-
162
+
160
163
  setTimeout(() => {
161
164
  this.isRestarting = false;
162
165
  if (reason) {
@@ -175,6 +178,9 @@ class Runner {
175
178
  * Stop the process and watcher
176
179
  */
177
180
  stop() {
181
+ // Cancel any active interactive prompt
182
+ cancelActivePrompt();
183
+
178
184
  if (this.watcher) {
179
185
  this.watcher.close();
180
186
  this.watcher = null;
@@ -228,7 +234,7 @@ class Runner {
228
234
  return;
229
235
  }
230
236
  this.lastErrorExplanationTime = now;
231
-
237
+
232
238
  // Don't explain startup errors that already caused process exit
233
239
  if (this.isProcessExiting) {
234
240
  return;
package/USER_MANUAL.md DELETED
@@ -1,106 +0,0 @@
1
- # 📖 NodeWise User Manual
2
-
3
- Welcome to **NodeWise**, your AI-powered companion for Node.js development. This manual will help you get the most out of NodeWise's error explanation and auto-restart features.
4
-
5
- ---
6
-
7
- ## 🚀 Getting Started
8
-
9
- NodeWise is designed to be a drop-in replacement for `node` or `nodemon` during development.
10
-
11
- ### Installation
12
- If you haven't already, you can link it globally or install it in your project:
13
- ```bash
14
- # Link for local development
15
- npm link
16
-
17
- # Install as a dev dependency
18
- npm install -D nodewise
19
- ```
20
-
21
- ### Initial Setup
22
- The first time you run NodeWise, it will launch a setup wizard to configure your preferred explanation mode.
23
- ```bash
24
- npx nodewise --setup
25
- ```
26
-
27
- ---
28
-
29
- ## 🤖 Explanation Modes
30
-
31
- ### 1. Gemini Explainer (AI-Powered)
32
- The "Top-G" mode. It uses Google's latest Gemini models to analyze your specific code and provide context-aware fixes.
33
-
34
- * **Setup**: Requires a free Gemini API Key from [Google AI Studio](https://aistudio.google.com/app/apikey).
35
- * **Best for**: Complex logic errors, obscure Node.js core errors, and step-by-step fix guides.
36
- * **Features**: Beautiful minimalist terminal output with neon highlighting.
37
-
38
- ### 2. Normal Detection (Offline)
39
- A lightning-fast, pattern-based mode that works entirely offline.
40
-
41
- * **Setup**: No setup required.
42
- * **Best for**: Common errors like `MODULE_NOT_FOUND`, `TypeError`, and `ReferenceError`.
43
- * **Features**: Zero latency, works without an internet connection.
44
-
45
- ---
46
-
47
- ## ⌨️ Command Line Interface
48
-
49
- | Command | Description |
50
- | :--- | :--- |
51
- | `nodewise <script.js>` | Run a script with auto-restart and error explainer. |
52
- | `nodewise --setup` | Change modes or update your Gemini API key. |
53
- | `nodewise --reset` | Wipe configuration and start fresh. |
54
- | `nodewise --help` | Show available options and examples. |
55
- | `nodewise -v` | Check your current NodeWise version. |
56
-
57
- ### Running with Arguments
58
- You can pass arguments to your script just like you would with node:
59
- ```bash
60
- nodewise server.js --port 3000 --env production
61
- ```
62
-
63
- ---
64
-
65
- ## ⚙️ Configuration
66
- NodeWise saves your preferences in `nodewise.config.json` in your project root.
67
-
68
- ```json
69
- {
70
- "mode": "gemini",
71
- "gemini": {
72
- "apiKey": "YOUR_KEY_HERE"
73
- },
74
- "autoRestart": true,
75
- "ignorePatterns": ["node_modules", ".git"],
76
- "timeout": 60000
77
- }
78
- ```
79
-
80
- * **autoRestart**: Set to `false` if you don't want the process to restart on file changes.
81
- * **timeout**: Increase this if the AI is taking too long to respond on slow connections.
82
-
83
- ---
84
-
85
- ## ✨ Pro Tips
86
-
87
- ### 1. Minimalist AI Insight
88
- When a crash occurs in Gemini mode, you'll see a clean, gapped output. We've removed bulky boxes to keep your terminal clean. Look for the **✦ GEMINI INTELLIGENCE** header.
89
-
90
- ### 2. Auto-Restart
91
- NodeWise watches your `.js` and `.json` files. As soon as you save a fix, NodeWise will instantly restart your application, allowing for a rapid "Code-Crash-Fix-Repeat" cycle.
92
-
93
- ### 3. Graceful Exit
94
- Use `Ctrl+C` to shut down both your application and the NodeWise watcher gracefully.
95
-
96
- ---
97
-
98
- ## 🆘 Troubleshooting
99
-
100
- * **API Timeouts**: If you see "Gemini timed out," check your internet connection or increase the `timeout` in `nodewise.config.json`.
101
- * **Invalid Key**: Ensure your API key is correct by re-running `nodewise --setup`.
102
- * **Files not watching**: Ensure the directory you are working in doesn't have an ignore pattern matching your files.
103
-
104
- ---
105
-
106
- *Made with ❤️ for Node.js developers.*