code-quality-lib 1.0.4 → 1.1.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 +28 -0
  2. package/index.js +134 -301
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,6 +18,9 @@
18
18
  - ⚙️ Configurable tools and commands
19
19
  - 📚 TypeScript definitions included
20
20
  - 🔧 CLI and library usage
21
+ - 📄 Detailed error reports for developers and AI agents
22
+ - 🔍 --logs flag for verbose terminal output
23
+ - 🤖 AI-friendly structured error information
21
24
 
22
25
  ## Installation
23
26
 
@@ -91,6 +94,9 @@ yarn add -D code-quality-lib && yarn code-quality
91
94
  # Run all quality checks
92
95
  code-quality
93
96
 
97
+ # Run with detailed error logs in terminal
98
+ code-quality --logs
99
+
94
100
  # Or use with npx (without installing)
95
101
  npx code-quality-lib
96
102
 
@@ -101,6 +107,28 @@ bunx code-quality-lib
101
107
  yarn code-quality
102
108
  ```
103
109
 
110
+ ### Error Reporting
111
+
112
+ The library automatically generates a detailed error report at `.quality-report.md` with:
113
+ - ✅ Status of each quality check
114
+ - 📋 Full error output for failed checks
115
+ - 💡 Suggestions for fixing common issues
116
+ - 🤖 AI-friendly structured information
117
+
118
+ **Viewing Errors:**
119
+ ```bash
120
+ # Silent mode (default) - errors saved to .quality-report.md
121
+ code-quality
122
+
123
+ # Verbose mode - errors shown in terminal + saved to report
124
+ code-quality --logs
125
+
126
+ # View the report
127
+ cat .quality-report.md
128
+ ```
129
+
130
+ **Note:** Add `.quality-report.md` to your `.gitignore` to keep reports local only.
131
+
104
132
  ### As a Library
105
133
 
106
134
  ```javascript
package/index.js CHANGED
@@ -4,6 +4,10 @@ const { execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
 
7
+ // Parse command line arguments
8
+ const args = process.argv.slice(2);
9
+ const showLogs = args.includes('--logs');
10
+
7
11
  // Load environment variables from .env file
8
12
  function loadEnvFile() {
9
13
  try {
@@ -29,326 +33,155 @@ function loadEnvFile() {
29
33
  }
30
34
  }
31
35
 
32
- // Check if this is first run and setup configuration
33
- function checkFirstRun() {
34
- const configPath = path.join(process.cwd(), '.code-quality.json');
35
-
36
- if (!fs.existsSync(configPath)) {
37
- colorLog('\n🔧 Code Quality Library - First Time Setup', 'bright');
38
- colorLog('─'.repeat(50), 'cyan');
39
- colorLog('\nChoose your quality rules configuration:', 'yellow');
40
- colorLog('1) Use library rules (recommended for new projects)', 'white');
41
- colorLog('2) Use project rules (keep existing configurations)', 'white');
42
- colorLog('3) Custom setup (choose specific tools)', 'white');
43
-
44
- // For now, default to library rules
45
- // In a real implementation, you would read user input here
46
- const choice = process.env.CODE_QUALITY_CHOICE || '1';
47
-
48
- const config = {
49
- useLibraryRules: choice === '1',
50
- useProjectRules: choice === '2',
51
- customSetup: choice === '3',
52
- tools: ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
53
- copyConfigs: choice === '1',
54
- version: '1.0.1'
55
- };
56
-
57
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
58
-
59
- if (choice === '1') {
60
- colorLog('\n✅ Using library rules - Config files will be copied', 'green');
61
- } else if (choice === '2') {
62
- colorLog('\n✅ Using project rules - Existing configs preserved', 'green');
63
- } else {
64
- colorLog('\n✅ Custom setup - Configure as needed', 'green');
65
- }
66
-
67
- colorLog('\n💡 To change this later, edit: .code-quality.json', 'cyan');
68
- colorLog('💡 Or run: code-quality --config to reconfigure', 'cyan');
69
- colorLog('─'.repeat(50), 'cyan');
70
-
71
- return config;
72
- }
73
-
74
- try {
75
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
76
- } catch (error) {
77
- colorLog('⚠️ Invalid config file, using defaults', 'yellow');
78
- return {
79
- useLibraryRules: true,
80
- useProjectRules: false,
81
- customSetup: false,
82
- tools: ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
83
- copyConfigs: true
84
- };
85
- }
86
- }
36
+ loadEnvFile();
87
37
 
88
- // Copy config files from library to project
89
- function copyConfigFiles(packageManager) {
90
- const libPath = path.dirname(__dirname);
91
- const projectPath = process.cwd();
92
-
93
- const configs = [
94
- { src: '.eslintrc.js', dest: '.eslintrc.js' },
95
- { src: '.prettierrc', dest: '.prettierrc' },
96
- { src: 'knip.json', dest: 'knip.json' },
97
- { src: 'tsconfig.json', dest: 'tsconfig.json' }
98
- ];
99
-
100
- configs.forEach(({ src, dest }) => {
101
- const srcPath = path.join(libPath, src);
102
- const destPath = path.join(projectPath, dest);
103
-
104
- try {
105
- if (fs.existsSync(srcPath)) {
106
- fs.copyFileSync(srcPath, destPath);
107
- colorLog(`✅ Copied ${dest}`, 'green');
108
- }
109
- } catch (error) {
110
- colorLog(`⚠️ Could not copy ${dest}: ${error.message}`, 'yellow');
111
- }
112
- });
113
- }
38
+ console.log('\n🔍 Professional Code Quality Check\n');
39
+ console.log('─'.repeat(50));
40
+ console.log('📦 Using bun package manager\n');
114
41
 
115
- // Color codes for beautiful output
116
- const colors = {
117
- reset: '\x1b[0m',
118
- bright: '\x1b[1m',
119
- red: '\x1b[31m',
120
- green: '\x1b[32m',
121
- yellow: '\x1b[33m',
122
- blue: '\x1b[34m',
123
- magenta: '\x1b[35m',
124
- cyan: '\x1b[36m',
125
- white: '\x1b[37m',
126
- dim: '\x1b[2m',
127
- header: '\x1b[1m\x1b[36m'
128
- };
129
-
130
- function colorLog(message, color = 'reset') {
131
- console.log(`${colors[color]}${message}${colors.reset}`);
42
+ if (showLogs) {
43
+ console.log('📋 Detailed error logging enabled (--logs flag)\n');
132
44
  }
133
45
 
134
- // Detect package manager
135
- function detectPackageManager() {
136
- const cwd = process.cwd();
46
+ // Prepare report
47
+ const reportPath = path.join(process.cwd(), '.quality-report.md');
48
+ const timestamp = new Date().toISOString();
49
+ let reportContent = `# Code Quality Report\n\n`;
50
+ reportContent += `**Generated**: ${timestamp}\n`;
51
+ reportContent += `**Package Manager**: bun\n\n`;
52
+ reportContent += `---\n\n`;
53
+
54
+ // Run quality checks
55
+ const checks = [
56
+ { name: 'TypeScript', cmd: 'npx tsc --noEmit', description: 'Type checking and compilation' },
57
+ { name: 'ESLint', cmd: 'npx eslint . --ext .js,.jsx,.ts,.tsx', description: 'Code linting and style checking' },
58
+ { name: 'Prettier', cmd: 'npx prettier --check .', description: 'Code formatting validation' },
59
+ { name: 'Knip', cmd: 'npx knip', description: 'Dead code detection' },
60
+ { name: 'Snyk', cmd: 'npx snyk test --severity-threshold=high', description: 'Security vulnerability scanning' }
61
+ ];
62
+
63
+ let allPassed = true;
64
+ const results = [];
65
+
66
+ checks.forEach(({ name, cmd, description }) => {
67
+ console.log(`Running ${name}...`);
137
68
 
138
- if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {
139
- return 'bun';
140
- } else if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
141
- return 'pnpm';
142
- } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
143
- return 'yarn';
144
- } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {
145
- return 'npm';
146
- }
69
+ reportContent += `## ${name}\n\n`;
70
+ reportContent += `**Description**: ${description}\n\n`;
71
+ reportContent += `**Command**: \`${cmd}\`\n\n`;
147
72
 
148
- // Default to npm if no lock file found
149
- return 'npm';
150
- }
151
-
152
- // Get package manager run command
153
- function getRunCommand(packageManager) {
154
- switch (packageManager) {
155
- case 'bun': return 'bun run';
156
- case 'pnpm': return 'pnpm run';
157
- case 'yarn': return 'yarn';
158
- case 'npm': return 'npm run';
159
- default: return 'npm run';
160
- }
161
- }
162
-
163
- // Get package manager exec command
164
- function getExecCommand(packageManager) {
165
- switch (packageManager) {
166
- case 'bun': return 'bunx';
167
- case 'pnpm': return 'pnpm dlx';
168
- case 'yarn': return 'yarn dlx';
169
- case 'npm': return 'npx';
170
- default: return 'npx';
171
- }
172
- }
173
-
174
- // Main class for the library
175
- class CodeQualityChecker {
176
- constructor(options = {}) {
177
- // Check first run configuration
178
- const config = checkFirstRun();
179
-
180
- this.options = {
181
- loadEnv: true,
182
- tools: config.tools || ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
183
- packageManager: detectPackageManager(),
184
- copyConfigs: config.copyConfigs !== false,
185
- useLibraryRules: config.useLibraryRules !== false,
186
- useProjectRules: config.useProjectRules || false,
187
- customSetup: config.customSetup || false,
188
- ...options
189
- };
73
+ try {
74
+ const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
75
+ console.log(`✅ ${name}: Passed`);
190
76
 
191
- if (this.options.loadEnv) {
192
- loadEnvFile();
77
+ reportContent += `**Status**: ✅ **PASSED**\n\n`;
78
+ if (output && output.trim()) {
79
+ reportContent += `**Output**:\n\`\`\`\n${output.trim()}\n\`\`\`\n\n`;
80
+
81
+ if (showLogs) {
82
+ console.log(`\n📄 ${name} Output:`);
83
+ console.log(output.trim());
84
+ console.log('');
85
+ }
193
86
  }
194
87
 
195
- if (this.options.copyConfigs && this.options.useLibraryRules) {
196
- copyConfigFiles(this.options.packageManager);
197
- }
198
- }
199
-
200
- runCommand(command, description) {
201
- const startTime = Date.now();
202
- const result = {
203
- success: true,
204
- message: ''
205
- };
88
+ results.push({ name, status: 'passed', output: output.trim() });
89
+ } catch (error) {
90
+ allPassed = false;
91
+ const errorOutput = error.stdout || error.stderr || error.message || 'Unknown error';
92
+ console.log(`❌ ${name}: Failed`);
206
93
 
207
- try {
208
- const output = execSync(command, { encoding: 'utf8' });
209
- result.message = output.trim();
210
- } catch (error) {
211
- result.success = false;
212
- result.message = error.stdout.trim();
94
+ // Show errors in terminal if --logs flag is present
95
+ if (showLogs) {
96
+ console.log(`\n❌ ${name} Error Details:`);
97
+ console.log('─'.repeat(50));
98
+ console.log(errorOutput.trim());
99
+ console.log('─'.repeat(50));
100
+ console.log('');
213
101
  }
214
102
 
215
- const endTime = Date.now();
216
- const duration = (endTime - startTime) / 1000;
217
-
218
- return {
219
- ...result,
220
- duration
221
- };
222
- }
223
-
224
- formatOutput(tool, result) {
225
- return formatOutput(tool, result);
226
- }
227
-
228
- checkSnykToken() {
229
- return checkSnykToken();
230
- }
231
-
232
- async run() {
233
- console.log(colors.header('\n🔍 Professional Code Quality Check\n'));
234
- console.log(colors.dim('─'.repeat(50)));
103
+ reportContent += `**Status**: ❌ **FAILED**\n\n`;
104
+ reportContent += `**Error Output**:\n\`\`\`\n${errorOutput.trim()}\n\`\`\`\n\n`;
235
105
 
236
- // Show detected package manager
237
- console.log(colors.dim(`📦 Using ${this.options.packageManager} package manager`));
238
-
239
- // Check Snyk authentication first
240
- const snykAuthenticated = this.checkSnykToken();
241
-
242
- const results = this.options.tools.map(tool => ({
243
- tool,
244
- command: tool === 'Snyk' && !snykAuthenticated
245
- ? `echo "Authentication required - add SNYK_TOKEN to .env or run ${getExecCommand(this.options.packageManager)} snyk auth"`
246
- : this.options.commands[tool],
247
- description: this.options.descriptions[tool]
248
- }));
249
-
250
- let allPassed = true;
251
- const outputs = [];
252
-
253
- for (const { tool, command, description } of results) {
254
- const result = this.runCommand(command, description);
255
- const output = this.formatOutput(tool, result);
256
- outputs.push(output);
257
- if (!result.success) {
258
- allPassed = false;
259
- }
260
- }
261
-
262
- const args = process.argv.slice(2);
263
- for (const arg of args) {
264
- if (arg === '--no-configs') {
265
- this.options.copyConfigs = false;
266
- } else if (arg === '--config') {
267
- this.options.reconfigure = true;
268
- }
269
- }
270
-
271
- console.log('\n');
272
- outputs.forEach((output) => console.log(output));
273
-
274
- console.log(colors.dim('─'.repeat(50)));
275
-
276
- if (allPassed) {
277
- console.log(
278
- '\n🎉 All quality checks passed! Code is ready for production.\n'
279
- );
280
- return { success: true, message: 'All checks passed' };
281
- } else {
282
- console.log(
283
- '\n❌ Some quality checks failed. Please fix the issues above.\n'
284
- );
285
- return { success: false, message: 'Some checks failed' };
106
+ // Add suggestions for common issues
107
+ reportContent += `**Suggestions**:\n`;
108
+ if (name === 'TypeScript') {
109
+ reportContent += `- Check type errors in the output above\n`;
110
+ reportContent += `- Run \`npx tsc --noEmit\` to see detailed errors\n`;
111
+ reportContent += `- Fix type mismatches and missing type definitions\n`;
112
+ } else if (name === 'ESLint') {
113
+ reportContent += `- Run \`bun run lint:fix\` to auto-fix issues\n`;
114
+ reportContent += `- Check ESLint errors in the output above\n`;
115
+ reportContent += `- Review and fix code style violations\n`;
116
+ } else if (name === 'Prettier') {
117
+ reportContent += `- Run \`bun run format:fix\` to auto-format files\n`;
118
+ reportContent += `- Check formatting issues in the output above\n`;
119
+ } else if (name === 'Knip') {
120
+ reportContent += `- Remove unused exports and dependencies\n`;
121
+ reportContent += `- Check dead code in the output above\n`;
122
+ } else if (name === 'Snyk') {
123
+ reportContent += `- Review security vulnerabilities in the output above\n`;
124
+ reportContent += `- Update vulnerable dependencies\n`;
125
+ reportContent += `- Run \`npx snyk wizard\` for guided fixes\n`;
286
126
  }
127
+ reportContent += `\n`;
128
+
129
+ results.push({ name, status: 'failed', error: errorOutput.trim() });
287
130
  }
131
+
132
+ reportContent += `---\n\n`;
133
+ });
134
+
135
+ // Add summary
136
+ reportContent += `## Summary\n\n`;
137
+ reportContent += `**Total Checks**: ${checks.length}\n`;
138
+ reportContent += `**Passed**: ${results.filter(r => r.status === 'passed').length}\n`;
139
+ reportContent += `**Failed**: ${results.filter(r => r.status === 'failed').length}\n\n`;
140
+
141
+ if (allPassed) {
142
+ reportContent += `### ✅ All quality checks passed!\n\n`;
143
+ reportContent += `Your code is ready for production.\n\n`;
144
+ } else {
145
+ reportContent += `### ❌ Some quality checks failed\n\n`;
146
+ reportContent += `Please review the errors above and fix the issues.\n\n`;
147
+ reportContent += `**Quick Fix Commands**:\n`;
148
+ reportContent += `- \`bun run fix\` - Auto-fix linting and formatting\n`;
149
+ reportContent += `- \`bun run type:check\` - Check TypeScript errors\n`;
150
+ reportContent += `- \`bun run lint:check\` - Check ESLint errors\n`;
151
+ reportContent += `- \`bun run format:check\` - Check Prettier formatting\n\n`;
288
152
  }
289
153
 
290
- // Export both the class and a simple function
291
- module.exports = { CodeQualityChecker };
292
-
293
- // If run directly, execute with default options
294
- if (require.main === module) {
295
- const args = process.argv.slice(2);
296
- const options = {};
297
-
298
- // Check for reconfigure flag
299
- if (args.includes('--config')) {
300
- // Remove existing config and run setup again
301
- const configPath = path.join(process.cwd(), '.code-quality.json');
302
- if (fs.existsSync(configPath)) {
303
- fs.unlinkSync(configPath);
304
- colorLog('🔧 Configuration reset - Running setup again', 'yellow');
305
- }
306
- }
307
-
308
- // Parse command line arguments
309
- for (let i = 0; i < args.length; i++) {
310
- const arg = args[i];
311
- if (arg === '--no-env') {
312
- options.loadEnv = false;
313
- } else if (arg === '--no-configs') {
314
- options.copyConfigs = false;
315
- } else if (arg.startsWith('--tools=')) {
316
- options.tools = arg.split('=')[1].split(',');
317
- } else if (arg === '--help' || arg === '-h') {
318
- console.log(`
319
- Professional Code Quality Checker
154
+ // Add AI Agent section
155
+ reportContent += `---\n\n`;
156
+ reportContent += `## For AI Agents\n\n`;
157
+ reportContent += `This report contains detailed error information for automated code quality fixes.\n\n`;
158
+ reportContent += `**Failed Checks**: ${results.filter(r => r.status === 'failed').map(r => r.name).join(', ') || 'None'}\n\n`;
159
+ if (!allPassed) {
160
+ reportContent += `**Action Required**:\n`;
161
+ results.filter(r => r.status === 'failed').forEach(r => {
162
+ reportContent += `- Fix ${r.name} errors\n`;
163
+ });
164
+ reportContent += `\n`;
165
+ }
320
166
 
321
- Usage: code-quality [options]
167
+ // Write report to file (overwrite existing)
168
+ try {
169
+ fs.writeFileSync(reportPath, reportContent, 'utf8');
170
+ console.log(`\n📄 Quality report saved to: .quality-report.md`);
171
+ } catch (error) {
172
+ console.error(`\n⚠️ Failed to write report: ${error.message}`);
173
+ }
322
174
 
323
- Options:
324
- --no-env Skip loading .env file
325
- --no-configs Skip copying config files
326
- --config Reconfigure setup choices
327
- --tools=tools Comma-separated list of tools to run
328
- (TypeScript,ESLint,Prettier,Knip,Snyk)
329
- --help, -h Show this help message
175
+ console.log('\n' + '─'.repeat(50));
330
176
 
331
- Examples:
332
- code-quality # Run all tools
333
- code-quality --tools=ESLint # Run only ESLint
334
- code-quality --no-env # Skip .env loading
335
- code-quality --config # Reconfigure setup
336
- `);
337
- process.exit(0);
338
- }
177
+ if (allPassed) {
178
+ console.log('\n🎉 All quality checks passed! Code is ready for production.\n');
179
+ process.exit(0);
180
+ } else {
181
+ console.log('\n❌ Some quality checks failed. Please fix the issues above.\n');
182
+ if (!showLogs) {
183
+ console.log('💡 Run with --logs flag to see detailed errors in terminal');
339
184
  }
340
-
341
- if (args.includes('--version') || args.includes('-v')) {
342
- const pkg = require('./package.json');
343
- console.log(pkg.version);
344
- process.exit(0);
345
- }
346
-
347
- const checker = new CodeQualityChecker(options);
348
- checker.run().then(({ success }) => {
349
- process.exit(success ? 0 : 1);
350
- }).catch(error => {
351
- console.error('Error running quality checks:', error);
352
- process.exit(1);
353
- });
185
+ console.log('📄 See .quality-report.md for detailed error information\n');
186
+ process.exit(1);
354
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-quality-lib",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "A configurable code quality checker library for Node.js projects",
5
5
  "main": "index.js",
6
6
  "bin": {