claude-flow-novice 2.18.24 → 2.18.26
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/config/agent-whitelist.json +281 -0
- package/config/default.yml +180 -0
- package/config/feature-flags.json +315 -0
- package/config/fix-reports/config-manager-custom-keys.json +15 -0
- package/config/hooks/post-edit-pipeline.js +897 -0
- package/config/hooks/post-edit-pipeline.js.original +612 -0
- package/config/kong/grafana/datasources/prometheus.yml +24 -0
- package/config/kong/kong.yml +496 -0
- package/config/kong/prometheus.yml +49 -0
- package/config/logrotate.d/cfn-logs +221 -0
- package/config/loki/loki-config.yml +172 -0
- package/config/loki/retention.yml +107 -0
- package/config/mcp-servers.json +152 -0
- package/config/production.yml.example +72 -0
- package/config/prometheus.yml +85 -0
- package/config/promtail/promtail-config.yml +162 -0
- package/config/redis.conf +33 -0
- package/config/redis.config.js +115 -0
- package/config/skill-requirements.json +341 -0
- package/config/sla-definitions.test.yml +66 -0
- package/config/sla-definitions.yml +150 -0
- package/package.json +1 -1
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced Post-Edit Pipeline - Comprehensive Validation Hook
|
|
4
|
+
* Validates edited files with TypeScript, ESLint, Prettier, Security Analysis, and Code Metrics
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - TypeScript validation with error categorization
|
|
8
|
+
* - ESLint integration for code quality
|
|
9
|
+
* - Prettier formatting checks
|
|
10
|
+
* - Security analysis (eval, password logging, XSS detection)
|
|
11
|
+
* - Code metrics (lines, functions, classes, complexity)
|
|
12
|
+
* - Actionable recommendations engine
|
|
13
|
+
*
|
|
14
|
+
* Usage: node config/hooks/post-edit-pipeline.js <file_path> [--memory-key <key>] [--agent-id <id>]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { execSync } from 'child_process';
|
|
18
|
+
import { existsSync, readFileSync, appendFileSync, mkdirSync } from 'fs';
|
|
19
|
+
import { dirname, extname, resolve } from 'path';
|
|
20
|
+
|
|
21
|
+
// Parse arguments
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const filePath = args[0];
|
|
24
|
+
const memoryKeyIndex = args.indexOf('--memory-key');
|
|
25
|
+
const memoryKey = memoryKeyIndex >= 0 ? args[memoryKeyIndex + 1] : null;
|
|
26
|
+
const agentIdIndex = args.indexOf('--agent-id');
|
|
27
|
+
const agentId = agentIdIndex >= 0 ? args[agentIdIndex + 1] : null;
|
|
28
|
+
|
|
29
|
+
if (!filePath) {
|
|
30
|
+
console.error('Error: File path required');
|
|
31
|
+
console.error('Usage: node config/hooks/post-edit-pipeline.js <file_path> [--memory-key <key>] [--agent-id <id>]');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Ensure log directory exists
|
|
36
|
+
const logDir = '.artifacts/logs';
|
|
37
|
+
if (!existsSync(logDir)) {
|
|
38
|
+
mkdirSync(logDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const logFile = `${logDir}/post-edit-pipeline.log`;
|
|
42
|
+
|
|
43
|
+
function log(status, message, metadata = {}) {
|
|
44
|
+
const entry = JSON.stringify({
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
file: filePath,
|
|
47
|
+
status,
|
|
48
|
+
message,
|
|
49
|
+
memoryKey,
|
|
50
|
+
agentId,
|
|
51
|
+
...metadata
|
|
52
|
+
});
|
|
53
|
+
console.log(entry);
|
|
54
|
+
appendFileSync(logFile, entry + '\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if file exists
|
|
58
|
+
if (!existsSync(filePath)) {
|
|
59
|
+
log('ERROR', 'File not found', { path: filePath });
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Read file content for analysis
|
|
64
|
+
const fileContent = readFileSync(filePath, 'utf-8');
|
|
65
|
+
const ext = extname(filePath);
|
|
66
|
+
|
|
67
|
+
// Initialize results object
|
|
68
|
+
const results = {
|
|
69
|
+
typescript: null,
|
|
70
|
+
eslint: null,
|
|
71
|
+
prettier: null,
|
|
72
|
+
security: null,
|
|
73
|
+
metrics: null,
|
|
74
|
+
recommendations: []
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// PHASE 1: TypeScript Validation
|
|
79
|
+
// ============================================================================
|
|
80
|
+
|
|
81
|
+
if (['.ts', '.tsx'].includes(ext)) {
|
|
82
|
+
try {
|
|
83
|
+
log('VALIDATING', 'Running TypeScript validation');
|
|
84
|
+
|
|
85
|
+
// Use tsc to check only this file
|
|
86
|
+
const cmd = `npx tsc --noEmit --skipLibCheck ${filePath}`;
|
|
87
|
+
execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' });
|
|
88
|
+
|
|
89
|
+
results.typescript = {
|
|
90
|
+
passed: true,
|
|
91
|
+
errors: []
|
|
92
|
+
};
|
|
93
|
+
log('SUCCESS', 'TypeScript validation passed');
|
|
94
|
+
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// Parse TypeScript errors
|
|
97
|
+
const output = error.stdout || error.stderr || '';
|
|
98
|
+
const lines = output.split('\n').filter(line => line.includes('error TS'));
|
|
99
|
+
|
|
100
|
+
if (lines.length === 0) {
|
|
101
|
+
results.typescript = {
|
|
102
|
+
passed: true,
|
|
103
|
+
errors: []
|
|
104
|
+
};
|
|
105
|
+
log('SUCCESS', 'No TypeScript errors detected');
|
|
106
|
+
} else {
|
|
107
|
+
// Categorize errors
|
|
108
|
+
const errorTypes = {
|
|
109
|
+
implicitAny: lines.filter(l => l.includes('TS7006') || l.includes('TS7031')).length,
|
|
110
|
+
propertyMissing: lines.filter(l => l.includes('TS2339')).length,
|
|
111
|
+
typeMismatch: lines.filter(l => l.includes('TS2322') || l.includes('TS2345')).length,
|
|
112
|
+
syntaxError: lines.filter(l => l.includes('TS1005') || l.includes('TS1128')).length,
|
|
113
|
+
other: 0
|
|
114
|
+
};
|
|
115
|
+
errorTypes.other = lines.length - Object.values(errorTypes).reduce((a, b) => a + b, 0);
|
|
116
|
+
|
|
117
|
+
const severity = errorTypes.syntaxError > 0 ? 'SYNTAX_ERROR' :
|
|
118
|
+
lines.length > 5 ? 'LINT_ISSUES' : 'TYPE_WARNING';
|
|
119
|
+
|
|
120
|
+
results.typescript = {
|
|
121
|
+
passed: false,
|
|
122
|
+
errorCount: lines.length,
|
|
123
|
+
errorTypes,
|
|
124
|
+
errors: lines.slice(0, 5),
|
|
125
|
+
severity
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
log(severity, `TypeScript errors detected: ${lines.length}`, {
|
|
129
|
+
errorCount: lines.length,
|
|
130
|
+
errorTypes,
|
|
131
|
+
errors: lines.slice(0, 5)
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Add recommendations based on error types
|
|
135
|
+
if (errorTypes.syntaxError > 0) {
|
|
136
|
+
results.recommendations.push({
|
|
137
|
+
type: 'typescript',
|
|
138
|
+
priority: 'critical',
|
|
139
|
+
message: 'Fix syntax errors before proceeding',
|
|
140
|
+
action: 'Review and fix TypeScript syntax issues'
|
|
141
|
+
});
|
|
142
|
+
} else if (errorTypes.implicitAny > 0) {
|
|
143
|
+
results.recommendations.push({
|
|
144
|
+
type: 'typescript',
|
|
145
|
+
priority: 'high',
|
|
146
|
+
message: 'Add explicit type annotations',
|
|
147
|
+
action: 'Add type annotations for parameters and return values'
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
log('SKIPPED', 'TypeScript validation skipped for non-TypeScript file');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// PHASE 1: ESLint Integration
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
|
|
161
|
+
try {
|
|
162
|
+
log('VALIDATING', 'Running ESLint validation');
|
|
163
|
+
|
|
164
|
+
// Check if ESLint is available
|
|
165
|
+
try {
|
|
166
|
+
execSync('npx eslint --version', { stdio: 'ignore', timeout: 5000 });
|
|
167
|
+
} catch {
|
|
168
|
+
log('SKIPPED', 'ESLint not available');
|
|
169
|
+
results.eslint = { available: false };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (results.eslint === null) {
|
|
173
|
+
// Run ESLint
|
|
174
|
+
const eslintCmd = `npx eslint "${filePath}" --format json`;
|
|
175
|
+
const eslintOutput = execSync(eslintCmd, {
|
|
176
|
+
stdio: 'pipe',
|
|
177
|
+
encoding: 'utf-8',
|
|
178
|
+
timeout: 10000
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const eslintResults = JSON.parse(eslintOutput);
|
|
182
|
+
const fileResults = eslintResults[0] || { messages: [] };
|
|
183
|
+
|
|
184
|
+
results.eslint = {
|
|
185
|
+
available: true,
|
|
186
|
+
passed: fileResults.errorCount === 0,
|
|
187
|
+
errorCount: fileResults.errorCount || 0,
|
|
188
|
+
warningCount: fileResults.warningCount || 0,
|
|
189
|
+
messages: fileResults.messages.slice(0, 5)
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (fileResults.errorCount > 0) {
|
|
193
|
+
log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
|
|
194
|
+
errorCount: fileResults.errorCount,
|
|
195
|
+
warningCount: fileResults.warningCount
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
results.recommendations.push({
|
|
199
|
+
type: 'eslint',
|
|
200
|
+
priority: 'high',
|
|
201
|
+
message: `Fix ${fileResults.errorCount} ESLint errors`,
|
|
202
|
+
action: `Run: npx eslint "${filePath}" --fix`
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
log('SUCCESS', 'ESLint validation passed');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
// ESLint errors are expected, parse them
|
|
210
|
+
if (error.stdout) {
|
|
211
|
+
try {
|
|
212
|
+
const eslintResults = JSON.parse(error.stdout);
|
|
213
|
+
const fileResults = eslintResults[0] || { messages: [] };
|
|
214
|
+
|
|
215
|
+
results.eslint = {
|
|
216
|
+
available: true,
|
|
217
|
+
passed: fileResults.errorCount === 0,
|
|
218
|
+
errorCount: fileResults.errorCount || 0,
|
|
219
|
+
warningCount: fileResults.warningCount || 0,
|
|
220
|
+
messages: fileResults.messages.slice(0, 5)
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
if (fileResults.errorCount > 0) {
|
|
224
|
+
log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
|
|
225
|
+
errorCount: fileResults.errorCount,
|
|
226
|
+
warningCount: fileResults.warningCount
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
results.recommendations.push({
|
|
230
|
+
type: 'eslint',
|
|
231
|
+
priority: 'high',
|
|
232
|
+
message: `Fix ${fileResults.errorCount} ESLint errors`,
|
|
233
|
+
action: `Run: npx eslint "${filePath}" --fix`
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
} catch {
|
|
237
|
+
log('ERROR', 'ESLint execution failed', { error: error.message });
|
|
238
|
+
results.eslint = { available: false, error: error.message };
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
log('ERROR', 'ESLint execution failed', { error: error.message });
|
|
242
|
+
results.eslint = { available: false, error: error.message };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ============================================================================
|
|
248
|
+
// PHASE 1: Prettier Integration
|
|
249
|
+
// ============================================================================
|
|
250
|
+
|
|
251
|
+
if (['.js', '.jsx', '.ts', '.tsx', '.json', '.css', '.html'].includes(ext)) {
|
|
252
|
+
try {
|
|
253
|
+
log('VALIDATING', 'Running Prettier formatting check');
|
|
254
|
+
|
|
255
|
+
// Check if Prettier is available
|
|
256
|
+
try {
|
|
257
|
+
execSync('npx prettier --version', { stdio: 'ignore', timeout: 5000 });
|
|
258
|
+
} catch {
|
|
259
|
+
log('SKIPPED', 'Prettier not available');
|
|
260
|
+
results.prettier = { available: false };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (results.prettier === null) {
|
|
264
|
+
// Run Prettier check
|
|
265
|
+
try {
|
|
266
|
+
const prettierCmd = `npx prettier --check "${filePath}"`;
|
|
267
|
+
execSync(prettierCmd, {
|
|
268
|
+
stdio: 'pipe',
|
|
269
|
+
encoding: 'utf-8',
|
|
270
|
+
timeout: 10000
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
results.prettier = {
|
|
274
|
+
available: true,
|
|
275
|
+
passed: true,
|
|
276
|
+
formatted: true
|
|
277
|
+
};
|
|
278
|
+
log('SUCCESS', 'Prettier formatting check passed');
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
results.prettier = {
|
|
282
|
+
available: true,
|
|
283
|
+
passed: false,
|
|
284
|
+
formatted: false,
|
|
285
|
+
needsFormatting: true
|
|
286
|
+
};
|
|
287
|
+
log('LINT_ISSUES', 'File needs Prettier formatting');
|
|
288
|
+
|
|
289
|
+
results.recommendations.push({
|
|
290
|
+
type: 'prettier',
|
|
291
|
+
priority: 'medium',
|
|
292
|
+
message: 'File needs formatting',
|
|
293
|
+
action: `Run: npx prettier --write "${filePath}"`
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
log('ERROR', 'Prettier execution failed', { error: error.message });
|
|
299
|
+
results.prettier = { available: false, error: error.message };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ============================================================================
|
|
304
|
+
// PHASE 2: Security Analysis
|
|
305
|
+
// ============================================================================
|
|
306
|
+
|
|
307
|
+
log('VALIDATING', 'Running security analysis');
|
|
308
|
+
|
|
309
|
+
const securityIssues = [];
|
|
310
|
+
|
|
311
|
+
// Check for eval() usage
|
|
312
|
+
if (fileContent.includes('eval(')) {
|
|
313
|
+
securityIssues.push({
|
|
314
|
+
type: 'security',
|
|
315
|
+
severity: 'critical',
|
|
316
|
+
message: 'Use of eval() function detected - security risk',
|
|
317
|
+
suggestion: 'Replace eval() with safer alternatives like JSON.parse() or Function constructor with proper validation'
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check for new Function() usage
|
|
322
|
+
if (fileContent.includes('new Function(')) {
|
|
323
|
+
securityIssues.push({
|
|
324
|
+
type: 'security',
|
|
325
|
+
severity: 'critical',
|
|
326
|
+
message: 'Use of new Function() detected - security risk',
|
|
327
|
+
suggestion: 'Avoid dynamic code execution; use safer alternatives'
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check for password logging
|
|
332
|
+
if (fileContent.includes('password') && (fileContent.includes('console.log') || fileContent.includes('console.debug'))) {
|
|
333
|
+
securityIssues.push({
|
|
334
|
+
type: 'security',
|
|
335
|
+
severity: 'critical',
|
|
336
|
+
message: 'Potential password logging detected',
|
|
337
|
+
suggestion: 'Remove password logging from code immediately'
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Check for XSS vulnerabilities (innerHTML with concatenation)
|
|
342
|
+
if (fileContent.includes('innerHTML') && fileContent.match(/innerHTML\s*[+]=|innerHTML\s*=\s*.*\+/)) {
|
|
343
|
+
securityIssues.push({
|
|
344
|
+
type: 'security',
|
|
345
|
+
severity: 'high',
|
|
346
|
+
message: 'Potential XSS vulnerability with innerHTML concatenation',
|
|
347
|
+
suggestion: 'Use textContent, createElement, or proper sanitization libraries'
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Check for hardcoded secrets (basic patterns)
|
|
352
|
+
const secretPatterns = [
|
|
353
|
+
/api[_-]?key\s*=\s*['"][^'"]{20,}['"]/i,
|
|
354
|
+
/secret\s*=\s*['"][^'"]{20,}['"]/i,
|
|
355
|
+
/token\s*=\s*['"][^'"]{20,}['"]/i,
|
|
356
|
+
/password\s*=\s*['"][^'"]+['"]/i
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
for (const pattern of secretPatterns) {
|
|
360
|
+
if (pattern.test(fileContent)) {
|
|
361
|
+
securityIssues.push({
|
|
362
|
+
type: 'security',
|
|
363
|
+
severity: 'critical',
|
|
364
|
+
message: 'Potential hardcoded secret detected',
|
|
365
|
+
suggestion: 'Move secrets to environment variables or secure configuration'
|
|
366
|
+
});
|
|
367
|
+
break; // Only report once
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
results.security = {
|
|
372
|
+
passed: securityIssues.length === 0,
|
|
373
|
+
issueCount: securityIssues.length,
|
|
374
|
+
issues: securityIssues
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
if (securityIssues.length > 0) {
|
|
378
|
+
log('SECURITY_ISSUES', `Security analysis found ${securityIssues.length} issues`, {
|
|
379
|
+
issueCount: securityIssues.length,
|
|
380
|
+
issues: securityIssues
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
results.recommendations.push({
|
|
384
|
+
type: 'security',
|
|
385
|
+
priority: 'critical',
|
|
386
|
+
message: `Address ${securityIssues.length} security ${securityIssues.length === 1 ? 'issue' : 'issues'} immediately`,
|
|
387
|
+
action: 'Review security recommendations and apply fixes'
|
|
388
|
+
});
|
|
389
|
+
} else {
|
|
390
|
+
log('SUCCESS', 'No security issues detected');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// PHASE 3: Code Metrics
|
|
395
|
+
// ============================================================================
|
|
396
|
+
|
|
397
|
+
log('VALIDATING', 'Calculating code metrics');
|
|
398
|
+
|
|
399
|
+
const lines = fileContent.split('\n');
|
|
400
|
+
const lineCount = lines.length;
|
|
401
|
+
const functionCount = (fileContent.match(/function\s+\w+|const\s+\w+\s*=\s*\([^)]*\)\s*=>/g) || []).length;
|
|
402
|
+
const classCount = (fileContent.match(/class\s+\w+/g) || []).length;
|
|
403
|
+
const todoCount = (fileContent.match(/\/\/\s*TODO|\/\*\s*TODO/gi) || []).length;
|
|
404
|
+
const fixmeCount = (fileContent.match(/\/\/\s*FIXME|\/\*\s*FIXME/gi) || []).length;
|
|
405
|
+
|
|
406
|
+
// Calculate cyclomatic complexity (simplified)
|
|
407
|
+
let complexity = 'low';
|
|
408
|
+
if (lineCount > 300 || functionCount > 10) {
|
|
409
|
+
complexity = 'high';
|
|
410
|
+
} else if (lineCount > 150 || functionCount > 5) {
|
|
411
|
+
complexity = 'medium';
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
results.metrics = {
|
|
415
|
+
lines: lineCount,
|
|
416
|
+
functions: functionCount,
|
|
417
|
+
classes: classCount,
|
|
418
|
+
todos: todoCount,
|
|
419
|
+
fixmes: fixmeCount,
|
|
420
|
+
complexity
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
log('SUCCESS', 'Code metrics calculated', {
|
|
424
|
+
lines: lineCount,
|
|
425
|
+
functions: functionCount,
|
|
426
|
+
classes: classCount,
|
|
427
|
+
complexity
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// PHASE 3: Recommendations Engine
|
|
432
|
+
// ============================================================================
|
|
433
|
+
|
|
434
|
+
log('VALIDATING', 'Generating recommendations');
|
|
435
|
+
|
|
436
|
+
// Maintainability recommendations
|
|
437
|
+
if (lineCount > 200) {
|
|
438
|
+
results.recommendations.push({
|
|
439
|
+
type: 'maintainability',
|
|
440
|
+
priority: 'medium',
|
|
441
|
+
message: `File has ${lineCount} lines - consider breaking it down`,
|
|
442
|
+
action: 'Split into smaller, focused modules (150-200 lines per file)'
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Code quality recommendations
|
|
447
|
+
if (fileContent.includes('var ')) {
|
|
448
|
+
results.recommendations.push({
|
|
449
|
+
type: 'code-quality',
|
|
450
|
+
priority: 'low',
|
|
451
|
+
message: 'Use const or let instead of var',
|
|
452
|
+
action: 'Replace var declarations with const or let for better scoping'
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (fileContent.includes('==') && !fileContent.includes('===') && fileContent.includes('==') > fileContent.includes('===')) {
|
|
457
|
+
results.recommendations.push({
|
|
458
|
+
type: 'code-quality',
|
|
459
|
+
priority: 'medium',
|
|
460
|
+
message: 'Prefer strict equality (===) over loose equality (==)',
|
|
461
|
+
action: 'Replace == with === for type-safe comparisons'
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// TypeScript-specific recommendations
|
|
466
|
+
if (['.ts', '.tsx'].includes(ext)) {
|
|
467
|
+
if (fileContent.includes(': any')) {
|
|
468
|
+
results.recommendations.push({
|
|
469
|
+
type: 'typescript',
|
|
470
|
+
priority: 'medium',
|
|
471
|
+
message: 'Avoid using "any" type when possible',
|
|
472
|
+
action: 'Use specific types or unknown for better type safety'
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!fileContent.includes('interface') && !fileContent.includes('type ') && lineCount > 100) {
|
|
477
|
+
results.recommendations.push({
|
|
478
|
+
type: 'typescript',
|
|
479
|
+
priority: 'low',
|
|
480
|
+
message: 'Consider defining interfaces or types',
|
|
481
|
+
action: 'Add type definitions for better code structure and maintainability'
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Documentation recommendations
|
|
487
|
+
if (fileContent.includes('export ') && !fileContent.includes('/**') && functionCount > 0) {
|
|
488
|
+
results.recommendations.push({
|
|
489
|
+
type: 'documentation',
|
|
490
|
+
priority: 'low',
|
|
491
|
+
message: 'Public exports could benefit from JSDoc comments',
|
|
492
|
+
action: 'Add JSDoc documentation for exported functions/classes'
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Testing recommendations
|
|
497
|
+
if (!filePath.includes('test') && !filePath.includes('spec') && (functionCount > 0 || classCount > 0)) {
|
|
498
|
+
results.recommendations.push({
|
|
499
|
+
type: 'testing',
|
|
500
|
+
priority: 'medium',
|
|
501
|
+
message: 'Consider writing tests for this module',
|
|
502
|
+
action: 'Create corresponding test file to ensure code reliability'
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// TODO/FIXME recommendations
|
|
507
|
+
if (todoCount > 0 || fixmeCount > 0) {
|
|
508
|
+
results.recommendations.push({
|
|
509
|
+
type: 'maintenance',
|
|
510
|
+
priority: 'low',
|
|
511
|
+
message: `Found ${todoCount} TODOs and ${fixmeCount} FIXMEs`,
|
|
512
|
+
action: 'Address pending tasks and technical debt'
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Sort recommendations by priority
|
|
517
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
518
|
+
results.recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
519
|
+
|
|
520
|
+
// Limit to top 10 recommendations
|
|
521
|
+
results.recommendations = results.recommendations.slice(0, 10);
|
|
522
|
+
|
|
523
|
+
log('SUCCESS', `Generated ${results.recommendations.length} recommendations`);
|
|
524
|
+
|
|
525
|
+
// ============================================================================
|
|
526
|
+
// Summary and Exit
|
|
527
|
+
// ============================================================================
|
|
528
|
+
|
|
529
|
+
// Calculate overall status
|
|
530
|
+
const hasCriticalIssues = results.recommendations.some(r => r.priority === 'critical');
|
|
531
|
+
const hasHighIssues = results.recommendations.some(r => r.priority === 'high');
|
|
532
|
+
const hasTypeScriptErrors = results.typescript && !results.typescript.passed;
|
|
533
|
+
const hasESLintErrors = results.eslint && results.eslint.errorCount > 0;
|
|
534
|
+
|
|
535
|
+
let overallStatus = 'SUCCESS';
|
|
536
|
+
let exitCode = 0;
|
|
537
|
+
|
|
538
|
+
if (hasCriticalIssues) {
|
|
539
|
+
overallStatus = 'CRITICAL_ISSUES';
|
|
540
|
+
exitCode = 2;
|
|
541
|
+
} else if (hasTypeScriptErrors && results.typescript.severity === 'SYNTAX_ERROR') {
|
|
542
|
+
overallStatus = 'SYNTAX_ERROR';
|
|
543
|
+
exitCode = 2;
|
|
544
|
+
} else if (hasHighIssues || hasESLintErrors) {
|
|
545
|
+
overallStatus = 'LINT_ISSUES';
|
|
546
|
+
exitCode = 0; // Non-blocking
|
|
547
|
+
} else if (results.recommendations.length > 0) {
|
|
548
|
+
overallStatus = 'IMPROVEMENTS_SUGGESTED';
|
|
549
|
+
exitCode = 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Log final summary
|
|
553
|
+
log(overallStatus, 'Pipeline validation complete', {
|
|
554
|
+
typescript: results.typescript,
|
|
555
|
+
eslint: results.eslint,
|
|
556
|
+
prettier: results.prettier,
|
|
557
|
+
security: results.security,
|
|
558
|
+
metrics: results.metrics,
|
|
559
|
+
recommendationCount: results.recommendations.length,
|
|
560
|
+
topRecommendations: results.recommendations.slice(0, 3)
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// Print user-friendly summary
|
|
564
|
+
console.error('\n' + '='.repeat(80));
|
|
565
|
+
console.error('Enhanced Post-Edit Pipeline Summary');
|
|
566
|
+
console.error('='.repeat(80));
|
|
567
|
+
|
|
568
|
+
if (results.typescript) {
|
|
569
|
+
console.error(`\nTypeScript: ${results.typescript.passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
570
|
+
if (!results.typescript.passed) {
|
|
571
|
+
console.error(` Errors: ${results.typescript.errorCount}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (results.eslint && results.eslint.available) {
|
|
576
|
+
console.error(`ESLint: ${results.eslint.passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
577
|
+
if (!results.eslint.passed) {
|
|
578
|
+
console.error(` Errors: ${results.eslint.errorCount}, Warnings: ${results.eslint.warningCount}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (results.prettier && results.prettier.available) {
|
|
583
|
+
console.error(`Prettier: ${results.prettier.passed ? '✅ PASSED' : '⚠️ NEEDS FORMATTING'}`);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
console.error(`\nSecurity: ${results.security.passed ? '✅ NO ISSUES' : '❌ ISSUES FOUND'}`);
|
|
587
|
+
if (!results.security.passed) {
|
|
588
|
+
console.error(` Issues: ${results.security.issueCount}`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
console.error(`\nCode Metrics:`);
|
|
592
|
+
console.error(` Lines: ${results.metrics.lines}`);
|
|
593
|
+
console.error(` Functions: ${results.metrics.functions}`);
|
|
594
|
+
console.error(` Classes: ${results.metrics.classes}`);
|
|
595
|
+
console.error(` Complexity: ${results.metrics.complexity.toUpperCase()}`);
|
|
596
|
+
|
|
597
|
+
if (results.recommendations.length > 0) {
|
|
598
|
+
console.error(`\nTop Recommendations:`);
|
|
599
|
+
results.recommendations.slice(0, 3).forEach((rec, i) => {
|
|
600
|
+
const icon = rec.priority === 'critical' ? '🔴' : rec.priority === 'high' ? '🟠' : rec.priority === 'medium' ? '🟡' : '🔵';
|
|
601
|
+
console.error(` ${icon} [${rec.type.toUpperCase()}] ${rec.message}`);
|
|
602
|
+
console.error(` Action: ${rec.action}`);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
if (results.recommendations.length > 3) {
|
|
606
|
+
console.error(` ... and ${results.recommendations.length - 3} more recommendations`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
console.error('='.repeat(80) + '\n');
|
|
611
|
+
|
|
612
|
+
process.exit(exitCode);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Kong Grafana Datasource Configuration
|
|
2
|
+
apiVersion: 1
|
|
3
|
+
|
|
4
|
+
datasources:
|
|
5
|
+
- name: Prometheus
|
|
6
|
+
type: prometheus
|
|
7
|
+
access: proxy
|
|
8
|
+
url: http://kong-prometheus:9090
|
|
9
|
+
isDefault: true
|
|
10
|
+
editable: true
|
|
11
|
+
jsonData:
|
|
12
|
+
timeInterval: "15s"
|
|
13
|
+
queryTimeout: "60s"
|
|
14
|
+
httpMethod: "POST"
|
|
15
|
+
|
|
16
|
+
- name: Kong-Redis
|
|
17
|
+
type: redis-datasource
|
|
18
|
+
access: proxy
|
|
19
|
+
url: redis://kong-redis:6379
|
|
20
|
+
editable: true
|
|
21
|
+
jsonData:
|
|
22
|
+
poolSize: 5
|
|
23
|
+
timeout: 10
|
|
24
|
+
pingInterval: 30
|