error-solution-ai-buddy 0.0.0-stage → 1.0.1

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/cli/index.js ADDED
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Error Solution AI Buddy - CLI Entry Point
5
+ *
6
+ * This is the main entry point for the CLI tool.
7
+ * It wraps commands (node, npm, java, dotnet) and intercepts errors.
8
+ *
9
+ * Usage: errbuddy <command> [args]
10
+ * Example: errbuddy node app.js
11
+ * errbuddy npm start
12
+ * errbuddy java MyApp
13
+ * errbuddy dotnet run
14
+ */
15
+
16
+ import { Command } from 'commander';
17
+ import { spawn } from 'child_process';
18
+ import { ErrorListener } from './error-listener.js';
19
+ import { Formatter } from './formatter.js';
20
+ import { AIService } from '../backend/src/ai/ollama-service.js';
21
+ import { HistoryService } from '../backend/src/services/history-service.js';
22
+ import chalk from 'chalk';
23
+
24
+ const program = new Command();
25
+
26
+ // Configuration
27
+ const CONFIG = {
28
+ aiEnabled: true,
29
+ ollamaModel: 'qwen2.5:0.5b', // Small model that works on low-RAM systems
30
+ ollamaUrl: 'http://localhost:11434',
31
+ showSpinner: true,
32
+ maxErrorLength: 2000, // Truncate very long errors
33
+ };
34
+
35
+ program
36
+ .name('errbuddy')
37
+ .description('AI-powered error explanations directly in your terminal')
38
+ .version('1.0.0')
39
+ .enablePositionalOptions()
40
+ .passThroughOptions()
41
+ .argument('<command>', 'Command to run (node, npm, java, dotnet, etc.)')
42
+ .argument('[args...]', 'Arguments to pass to the command')
43
+ .option('--no-ai', 'Disable AI explanations (show errors only)')
44
+ .option('--model <model>', 'Ollama model to use', 'qwen2.5:0.5b')
45
+ .option('--offline', 'Force offline mode (pattern matching only)')
46
+ .action(async (command, args, options) => {
47
+ await runCommand(command, args, options);
48
+ });
49
+
50
+ // Help command with examples
51
+ program
52
+ .command('examples')
53
+ .description('Show usage examples')
54
+ .action(() => {
55
+ console.log(chalk.cyan('\nšŸ“š Error Buddy Examples:\n'));
56
+ console.log(' ' + chalk.green('errbuddy node app.js'));
57
+ console.log(' ' + chalk.gray('→ Run Node.js app and explain any errors\n'));
58
+ console.log(' ' + chalk.green('errbuddy npm start'));
59
+ console.log(' ' + chalk.gray('→ Run npm script with error explanations\n'));
60
+ console.log(' ' + chalk.green('errbuddy java MyClass'));
61
+ console.log(' ' + chalk.gray('→ Run Java program with error explanations\n'));
62
+ console.log(' ' + chalk.green('errbuddy dotnet run'));
63
+ console.log(' ' + chalk.gray('→ Run .NET project with error explanations\n'));
64
+ console.log(' ' + chalk.green('errbuddy --no-ai node app.js'));
65
+ console.log(' ' + chalk.gray('→ Run without AI (pattern matching only)\n'));
66
+ });
67
+
68
+ // Config command
69
+ program
70
+ .command('config')
71
+ .description('Show current configuration')
72
+ .action(() => {
73
+ console.log(chalk.cyan('\nāš™ļø Current Configuration:\n'));
74
+ console.log(` AI Enabled: ${CONFIG.aiEnabled ? chalk.green('Yes') : chalk.red('No')}`);
75
+ console.log(` Ollama Model: ${chalk.yellow(CONFIG.ollamaModel)}`);
76
+ console.log(` Ollama URL: ${chalk.gray(CONFIG.ollamaUrl)}`);
77
+ console.log('');
78
+ });
79
+
80
+ /**
81
+ * Sanitize error text before sending to AI
82
+ *
83
+ * WHY: Privacy protection - we don't want to leak:
84
+ * - File paths (might contain project names, usernames)
85
+ * - Environment variables
86
+ * - API keys that might appear in errors
87
+ */
88
+ function sanitizeError(errorText) {
89
+ let sanitized = errorText;
90
+
91
+ // Remove Windows-style absolute paths (C:\Users\...)
92
+ sanitized = sanitized.replace(/[A-Za-z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*/g, '[path]\\');
93
+
94
+ // Remove Unix-style absolute paths (/home/user/...)
95
+ sanitized = sanitized.replace(/\/(?:home|Users|var|usr|opt)\/[^\s:]+/g, '[path]');
96
+
97
+ // Remove common secret patterns
98
+ sanitized = sanitized.replace(/(?:api[_-]?key|secret|password|token)\s*[:=]\s*['"]?[^'"\s]+['"]?/gi, '[REDACTED]');
99
+
100
+ // Truncate if too long
101
+ if (sanitized.length > CONFIG.maxErrorLength) {
102
+ sanitized = sanitized.substring(0, CONFIG.maxErrorLength) + '\n... (truncated)';
103
+ }
104
+
105
+ return sanitized;
106
+ }
107
+
108
+ /**
109
+ * Main function that runs the wrapped command
110
+ *
111
+ * WHY we use spawn() instead of exec():
112
+ * - spawn() streams output in real-time (better UX)
113
+ * - exec() buffers everything (user waits too long)
114
+ */
115
+ async function runCommand(command, args, options) {
116
+ const formatter = new Formatter();
117
+ const errorListener = new ErrorListener();
118
+ const aiService = new AIService(CONFIG.ollamaUrl, options.model || CONFIG.ollamaModel);
119
+ const history = new HistoryService();
120
+
121
+ // Check if Ollama is available (non-blocking)
122
+ let ollamaAvailable = false;
123
+ if (!options.offline && options.ai !== false) {
124
+ ollamaAvailable = await aiService.checkConnection();
125
+ if (!ollamaAvailable) {
126
+ console.log(chalk.yellow('āš ļø Ollama not detected. Using pattern matching only.'));
127
+ console.log(chalk.gray(' For AI explanations (free, runs locally):'));
128
+ console.log(chalk.gray(' 1. Install Ollama: https://ollama.com/download'));
129
+ console.log(chalk.gray(` 2. Download a model: ollama pull ${options.model || CONFIG.ollamaModel}`));
130
+ console.log(chalk.gray(' 3. Start it: ollama serve\n'));
131
+ }
132
+ }
133
+
134
+ // Spawn the child process
135
+ // stdio: ['inherit', 'pipe', 'pipe'] means:
136
+ // - stdin: inherited (user can type input)
137
+ // - stdout: piped (we capture and forward it)
138
+ // - stderr: piped (we capture, analyze, then forward it)
139
+ //
140
+ // We join command + args into a single shell string to avoid Node.js 24+
141
+ // DEP0190 warning ("Passing args to a child process with shell option true
142
+ // can lead to security vulnerabilities"). This is safe here because the
143
+ // user controls the input (they typed the command themselves).
144
+ const fullCommand = [command, ...args].join(' ');
145
+ const child = spawn(fullCommand, [], {
146
+ stdio: ['inherit', 'pipe', 'pipe'],
147
+ shell: true, // Required for Windows compatibility
148
+ env: { ...process.env, FORCE_COLOR: '1' } // Preserve colors
149
+ });
150
+
151
+ let stderrBuffer = '';
152
+ let errorDetected = false;
153
+
154
+ // Forward stdout immediately (no delay)
155
+ child.stdout.on('data', (data) => {
156
+ process.stdout.write(data);
157
+ });
158
+
159
+ // Capture and analyze stderr
160
+ child.stderr.on('data', (data) => {
161
+ const text = data.toString();
162
+
163
+ // Always show the original error first
164
+ process.stderr.write(data);
165
+
166
+ // Buffer stderr for analysis
167
+ stderrBuffer += text;
168
+
169
+ // Check if this looks like an error
170
+ if (errorListener.detectError(text)) {
171
+ errorDetected = true;
172
+ }
173
+ });
174
+
175
+ // When process ends, analyze collected errors
176
+ child.on('close', async (exitCode) => {
177
+ if (errorDetected && stderrBuffer.trim()) {
178
+ // Analyze the error
179
+ const analysis = errorListener.analyzeError(stderrBuffer);
180
+
181
+ if (analysis.isError) {
182
+ console.log(''); // Blank line for readability
183
+
184
+ // Try AI explanation if available
185
+ if (ollamaAvailable && options.ai !== false) {
186
+ const explanation = await explainWithAI(stderrBuffer, analysis, aiService, formatter);
187
+ try { history.saveError({ ...analysis, fullText: stderrBuffer }, explanation, `${command} ${args.join(' ')}`); } catch {}
188
+ } else {
189
+ // Fall back to pattern-based explanation
190
+ formatter.printPatternExplanation(analysis);
191
+ try { history.saveError({ ...analysis, fullText: stderrBuffer }, analysis.localExplanation, `${command} ${args.join(' ')}`); } catch {}
192
+ }
193
+ }
194
+ }
195
+
196
+ // Exit with the same code as the child process
197
+ process.exit(exitCode);
198
+ });
199
+
200
+ // Handle spawn errors (command not found, etc.)
201
+ child.on('error', (err) => {
202
+ console.error(chalk.red(`\nāŒ Failed to run command: ${command}`));
203
+ console.error(chalk.gray(` ${err.message}`));
204
+
205
+ if (err.code === 'ENOENT') {
206
+ console.error(chalk.yellow(`\nšŸ’” Tip: Make sure "${command}" is installed and in your PATH.`));
207
+ }
208
+
209
+ process.exit(1);
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Get AI explanation for an error
215
+ */
216
+ async function explainWithAI(errorText, analysis, aiService, formatter) {
217
+ const ora = (await import('ora')).default;
218
+ const spinner = ora({
219
+ text: 'Getting AI explanation...',
220
+ color: 'cyan'
221
+ }).start();
222
+
223
+ try {
224
+ // Sanitize the error before sending to AI
225
+ const sanitizedError = sanitizeError(errorText);
226
+
227
+ const explanation = await aiService.explainError(sanitizedError, analysis.language);
228
+ spinner.stop();
229
+
230
+ formatter.printAIExplanation(explanation);
231
+ return explanation;
232
+ } catch (err) {
233
+ spinner.stop();
234
+ console.log(chalk.yellow('āš ļø AI explanation failed. Showing pattern-based help:'));
235
+ formatter.printPatternExplanation(analysis);
236
+ return analysis.localExplanation;
237
+ }
238
+ }
239
+
240
+ // Run the CLI
241
+ program.parse();
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "cli",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {}
6
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json CHANGED
@@ -1,6 +1,55 @@
1
1
  {
2
2
  "name": "error-solution-ai-buddy",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "AI-powered error explanation tool for developers - works directly in your terminal",
5
+ "type": "module",
6
+ "main": "cli/index.js",
7
+ "bin": {
8
+ "errbuddy": "./cli/index.js"
9
+ },
10
+ "files": [
11
+ "cli",
12
+ "backend",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "start": "node cli/index.js",
18
+ "test": "node --test tests/*.js",
19
+ "lint": "eslint cli/ backend/",
20
+ "demo": "node tests/samples/js-errors.js",
21
+ "prepublishOnly": "npm test"
22
+ },
23
+ "keywords": [
24
+ "error",
25
+ "debugging",
26
+ "ai",
27
+ "developer-tools",
28
+ "terminal",
29
+ "ollama",
30
+ "cli",
31
+ "llm",
32
+ "error-explainer",
33
+ "local-ai"
34
+ ],
35
+ "author": "Sithija Harshana Jayasinghe",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/sithija-jayasinghe/solution-ai-buddy-v1.git",
40
+ "directory": "error-solution-ai-buddy"
41
+ },
42
+ "homepage": "https://github.com/sithija-jayasinghe/solution-ai-buddy-v1#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/sithija-jayasinghe/solution-ai-buddy-v1/issues"
45
+ },
46
+ "dependencies": {
47
+ "chalk": "^5.3.0",
48
+ "commander": "^11.1.0",
49
+ "ora": "^7.0.1",
50
+ "boxen": "^7.1.1"
51
+ },
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ }
55
+ }