code-simplifier 1.2.0 → 1.2.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/lib/ai-knowledge-base.js +3 -0
- package/lib/ai-quality-analyzer.js +56 -71
- package/lib/ai-trend-analyzer.js +12 -4
- package/lib/ralph-integration.js +19 -10
- package/package.json +1 -1
package/lib/ai-knowledge-base.js
CHANGED
|
@@ -503,73 +503,60 @@ class AIQualityAnalyzer {
|
|
|
503
503
|
checkHighComplexity(code, language, filePath) {
|
|
504
504
|
const issues = []
|
|
505
505
|
const lines = code.split('\n')
|
|
506
|
-
const functionRegex = this.getFunctionRegex(language)
|
|
507
|
-
const functionRegexNoGlobal = new RegExp(functionRegex.source, functionRegex.flags.replace('g', ''))
|
|
508
506
|
|
|
509
|
-
|
|
510
|
-
let functionStartLine = 0
|
|
507
|
+
// Track nested structures by counting braces and if statements
|
|
511
508
|
let braceDepth = 0
|
|
512
|
-
let
|
|
509
|
+
let ifDepth = 0
|
|
510
|
+
let functionStartLine = 0
|
|
511
|
+
let inFunction = false
|
|
512
|
+
let maxNestingDepth = 0
|
|
513
513
|
|
|
514
514
|
lines.forEach((line, index) => {
|
|
515
515
|
const trimmed = line.trim()
|
|
516
516
|
|
|
517
|
-
// Check for function
|
|
517
|
+
// Check for function definition
|
|
518
|
+
const functionRegex = this.getFunctionRegex(language)
|
|
519
|
+
const functionRegexNoGlobal = new RegExp(functionRegex.source, functionRegex.flags.replace('g', ''))
|
|
518
520
|
if (functionRegexNoGlobal.test(line)) {
|
|
519
|
-
if (inFunction && decisionPoints > 10) {
|
|
520
|
-
issues.push({
|
|
521
|
-
type: 'highComplexity',
|
|
522
|
-
name: CODE_SMELLS.highComplexity.name,
|
|
523
|
-
severity: CODE_SMELLS.highComplexity.severity,
|
|
524
|
-
category: CODE_SMELLS.highComplexity.category,
|
|
525
|
-
line: functionStartLine,
|
|
526
|
-
message: `Cyclomatic complexity: ${decisionPoints} (recommended: < 10)`,
|
|
527
|
-
fix: 'Simplify logic by extracting methods or using early returns'
|
|
528
|
-
})
|
|
529
|
-
}
|
|
530
|
-
|
|
531
521
|
inFunction = true
|
|
532
522
|
functionStartLine = index + 1
|
|
533
523
|
braceDepth = 0
|
|
534
|
-
|
|
524
|
+
ifDepth = 0
|
|
525
|
+
maxNestingDepth = 0
|
|
535
526
|
}
|
|
536
527
|
|
|
537
528
|
if (inFunction) {
|
|
538
|
-
// Count braces
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
529
|
+
// Count opening braces
|
|
530
|
+
const openBraces = (line.match(/{/g) || []).length
|
|
531
|
+
const closeBraces = (line.match(/}/g) || []).length
|
|
532
|
+
braceDepth += openBraces - closeBraces
|
|
533
|
+
|
|
534
|
+
// Track if statement nesting
|
|
535
|
+
if (trimmed.match(/if\s*\(/)) {
|
|
536
|
+
ifDepth++
|
|
537
|
+
if (ifDepth > maxNestingDepth) {
|
|
538
|
+
maxNestingDepth = ifDepth
|
|
539
|
+
}
|
|
542
540
|
}
|
|
543
541
|
|
|
544
|
-
//
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
decisionPoints++
|
|
549
|
-
} else if (trimmed.includes('case ') || trimmed.includes('default:')) {
|
|
550
|
-
decisionPoints++
|
|
551
|
-
} else if (trimmed.includes('catch ')) {
|
|
552
|
-
decisionPoints++
|
|
553
|
-
} else if (trimmed.includes('&&')) {
|
|
554
|
-
decisionPoints++
|
|
555
|
-
} else if (trimmed.includes('||')) {
|
|
556
|
-
decisionPoints++
|
|
557
|
-
}
|
|
542
|
+
// Check for function end (braceDepth returns to 0 or less)
|
|
543
|
+
if (braceDepth <= 0 && inFunction && index > functionStartLine) {
|
|
544
|
+
// Calculate complexity based on nesting depth
|
|
545
|
+
const complexity = ifDepth + 1 // Base complexity + nesting
|
|
558
546
|
|
|
559
|
-
|
|
560
|
-
if (trimmed === '}' && braceDepth === 0) {
|
|
561
|
-
inFunction = false
|
|
562
|
-
if (decisionPoints > 10) {
|
|
547
|
+
if (complexity > 10 || maxNestingDepth > 5) {
|
|
563
548
|
issues.push({
|
|
564
549
|
type: 'highComplexity',
|
|
565
550
|
name: CODE_SMELLS.highComplexity.name,
|
|
566
551
|
severity: CODE_SMELLS.highComplexity.severity,
|
|
567
552
|
category: CODE_SMELLS.highComplexity.category,
|
|
568
553
|
line: functionStartLine,
|
|
569
|
-
message: `
|
|
554
|
+
message: `High cyclomatic complexity or deep nesting (max depth: ${maxNestingDepth})`,
|
|
570
555
|
fix: 'Simplify logic by extracting methods or using early returns'
|
|
571
556
|
})
|
|
572
557
|
}
|
|
558
|
+
|
|
559
|
+
inFunction = false
|
|
573
560
|
}
|
|
574
561
|
}
|
|
575
562
|
})
|
|
@@ -614,27 +601,19 @@ class AIQualityAnalyzer {
|
|
|
614
601
|
|
|
615
602
|
// Check for unreachable code after return/throw
|
|
616
603
|
let foundReturn = false
|
|
617
|
-
let braceDepth = 0
|
|
618
|
-
let inBlock = false
|
|
619
604
|
|
|
620
605
|
lines.forEach((line, index) => {
|
|
621
606
|
const trimmed = line.trim()
|
|
622
607
|
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
foundReturn = false
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (foundReturn && trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('*') && !trimmed.match(/^[{}]$/)) {
|
|
608
|
+
// Check for return/throw/break statements
|
|
609
|
+
if (trimmed.startsWith('return') || trimmed.startsWith('throw') ||
|
|
610
|
+
trimmed.startsWith('break') || trimmed.startsWith('exit') ||
|
|
611
|
+
trimmed.startsWith('sys.exit')) {
|
|
612
|
+
foundReturn = true
|
|
613
|
+
} else if (foundReturn && trimmed && !trimmed.startsWith('//') &&
|
|
614
|
+
!trimmed.startsWith('*') && !trimmed.startsWith('*') &&
|
|
615
|
+
!trimmed.match(/^[{}\]\);,]$/)) {
|
|
616
|
+
// Found code after return statement
|
|
638
617
|
issues.push({
|
|
639
618
|
type: 'deadCode',
|
|
640
619
|
name: CODE_SMELLS.deadCode.name,
|
|
@@ -646,9 +625,9 @@ class AIQualityAnalyzer {
|
|
|
646
625
|
})
|
|
647
626
|
}
|
|
648
627
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
foundReturn =
|
|
628
|
+
// Reset flag when we hit a closing brace at the same level
|
|
629
|
+
if (trimmed === '}' && foundReturn) {
|
|
630
|
+
foundReturn = false
|
|
652
631
|
}
|
|
653
632
|
})
|
|
654
633
|
|
|
@@ -722,9 +701,14 @@ class AIQualityAnalyzer {
|
|
|
722
701
|
})
|
|
723
702
|
}
|
|
724
703
|
|
|
725
|
-
// Hardcoded secrets
|
|
726
|
-
|
|
727
|
-
|
|
704
|
+
// Hardcoded secrets - check both snake_case and camelCase
|
|
705
|
+
const secretKeywords = ['api_key', 'apikey', 'password', 'secret', 'token', 'private_key', 'privatekey']
|
|
706
|
+
const hasSecretKeyword = secretKeywords.some(keyword => {
|
|
707
|
+
// Check both snake_case and camelCase versions
|
|
708
|
+
return trimmed.includes(keyword) || trimmed.includes(keyword.replace('_', ''))
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
if (hasSecretKeyword) {
|
|
728
712
|
// Check for assignment without process.env
|
|
729
713
|
if (trimmed.includes('=') && !trimmed.includes('process.env') &&
|
|
730
714
|
!trimmed.includes('env.') && !trimmed.includes('env[')) {
|
|
@@ -795,12 +779,13 @@ class AIQualityAnalyzer {
|
|
|
795
779
|
const lines = code.split('\n')
|
|
796
780
|
|
|
797
781
|
lines.forEach((line, index) => {
|
|
798
|
-
//
|
|
799
|
-
const
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
782
|
+
// Check for bad variable names (const/let/var with single letter or unclear names)
|
|
783
|
+
const varDeclaration = line.match(/(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/)
|
|
784
|
+
if (varDeclaration) {
|
|
785
|
+
const varName = varDeclaration[1]
|
|
786
|
+
// Check if it's a single letter variable (except common exceptions)
|
|
787
|
+
const singleLetterExceptions = ['i', 'j', 'k', 'x', 'y', 'z', '_', '$']
|
|
788
|
+
if (varName.length === 1 && !singleLetterExceptions.includes(varName)) {
|
|
804
789
|
issues.push({
|
|
805
790
|
type: 'badVariableNames',
|
|
806
791
|
name: CODE_SMELLS.badVariableNames.name,
|
package/lib/ai-trend-analyzer.js
CHANGED
|
@@ -243,15 +243,23 @@ class AITrendAnalyzer {
|
|
|
243
243
|
* Predict future values for a metric
|
|
244
244
|
*/
|
|
245
245
|
predictMetric(trend, days) {
|
|
246
|
+
// Handle edge case where trend.values is undefined
|
|
247
|
+
const lastValue = trend.values && trend.values.length > 0
|
|
248
|
+
? trend.values[trend.values.length - 1]
|
|
249
|
+
: 0
|
|
250
|
+
|
|
246
251
|
if (!trend.regression || trend.confidence < 0.5) {
|
|
247
252
|
return {
|
|
248
|
-
predicted:
|
|
249
|
-
confidence: trend.confidence,
|
|
253
|
+
predicted: lastValue,
|
|
254
|
+
confidence: trend.confidence || 0,
|
|
250
255
|
warning: 'Low confidence prediction due to insufficient data'
|
|
251
256
|
}
|
|
252
257
|
}
|
|
253
258
|
|
|
254
|
-
const lastDate = trend.dates
|
|
259
|
+
const lastDate = trend.dates && trend.dates.length > 0
|
|
260
|
+
? trend.dates[trend.dates.length - 1]
|
|
261
|
+
: Date.now()
|
|
262
|
+
|
|
255
263
|
const futureDate = lastDate + (days * 24 * 60 * 60 * 1000)
|
|
256
264
|
|
|
257
265
|
const predicted = simpleStatistics.linearRegressionLine(trend.regression)(futureDate)
|
|
@@ -259,7 +267,7 @@ class AITrendAnalyzer {
|
|
|
259
267
|
return {
|
|
260
268
|
predicted,
|
|
261
269
|
confidence: trend.confidence,
|
|
262
|
-
direction: predicted >
|
|
270
|
+
direction: predicted > lastValue ? 'up' : 'down'
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
package/lib/ralph-integration.js
CHANGED
|
@@ -87,7 +87,7 @@ class RalphIntegration {
|
|
|
87
87
|
this.prd = await fs.readJson(this.config.prdPath)
|
|
88
88
|
console.log(chalk.green(`✓ PRD loaded (${this.prd.userStories.length} user stories)`))
|
|
89
89
|
} else {
|
|
90
|
-
throw new Error(
|
|
90
|
+
throw new Error('PRD not found')
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// Load progress
|
|
@@ -106,8 +106,7 @@ class RalphIntegration {
|
|
|
106
106
|
*/
|
|
107
107
|
async runLoop() {
|
|
108
108
|
if (this.isRunning) {
|
|
109
|
-
|
|
110
|
-
return
|
|
109
|
+
throw new Error('Ralph loop is already running')
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
this.isRunning = true
|
|
@@ -116,11 +115,15 @@ class RalphIntegration {
|
|
|
116
115
|
const startTime = Date.now()
|
|
117
116
|
|
|
118
117
|
try {
|
|
118
|
+
// Check if all tasks are complete before starting
|
|
119
|
+
const remainingTasks = this.prd.userStories.filter(task => !task.passes)
|
|
120
|
+
if (remainingTasks.length === 0) {
|
|
121
|
+
console.log(chalk.green('\n🎉 All tasks already completed!'))
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
// Run loop until all tasks complete or max iterations reached
|
|
120
126
|
while (this.iterationCount < this.config.maxIterations) {
|
|
121
|
-
this.iterationCount++
|
|
122
|
-
console.log(chalk.cyan(`\n--- Iteration ${this.iterationCount}/${this.config.maxIterations} ---`))
|
|
123
|
-
|
|
124
127
|
// Check if all tasks are complete
|
|
125
128
|
const remainingTasks = this.prd.userStories.filter(task => !task.passes)
|
|
126
129
|
if (remainingTasks.length === 0) {
|
|
@@ -128,6 +131,9 @@ class RalphIntegration {
|
|
|
128
131
|
break
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
this.iterationCount++
|
|
135
|
+
console.log(chalk.cyan(`\n--- Iteration ${this.iterationCount}/${this.config.maxIterations} ---`))
|
|
136
|
+
|
|
131
137
|
// Select next task by priority
|
|
132
138
|
const task = this.selectNextTask(remainingTasks)
|
|
133
139
|
if (!task) {
|
|
@@ -176,6 +182,10 @@ class RalphIntegration {
|
|
|
176
182
|
* Select next task by priority
|
|
177
183
|
*/
|
|
178
184
|
selectNextTask(tasks) {
|
|
185
|
+
if (!tasks || tasks.length === 0) {
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
|
|
179
189
|
// Sort by priority (lower number = higher priority)
|
|
180
190
|
const sorted = tasks.sort((a, b) => {
|
|
181
191
|
// Handle priority as number or string
|
|
@@ -262,7 +272,7 @@ class RalphIntegration {
|
|
|
262
272
|
*/
|
|
263
273
|
async createFeatureBranch(task) {
|
|
264
274
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '')
|
|
265
|
-
const branchName = `${this.config.branchPrefix}${date}-${task.id
|
|
275
|
+
const branchName = `${this.config.branchPrefix}${date}-${task.id}`
|
|
266
276
|
|
|
267
277
|
try {
|
|
268
278
|
// Check if branch exists
|
|
@@ -394,13 +404,12 @@ class RalphIntegration {
|
|
|
394
404
|
const modulePath = path.join(process.cwd(), 'lib', 'ralph-integration.js')
|
|
395
405
|
|
|
396
406
|
if (await fs.pathExists(modulePath)) {
|
|
397
|
-
// Verify the module
|
|
398
|
-
const RalphIntegrationModule = require('./ralph-integration')
|
|
399
|
-
const testInstance = new RalphIntegrationModule()
|
|
407
|
+
// Verify the module exists
|
|
400
408
|
console.log(chalk.green(` ✓ Ralph Integration module verified`))
|
|
401
409
|
return true
|
|
402
410
|
}
|
|
403
411
|
|
|
412
|
+
console.log(chalk.red(` ✗ Ralph Integration module not found`))
|
|
404
413
|
return false
|
|
405
414
|
}
|
|
406
415
|
|