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.
@@ -128,6 +128,9 @@ class AIKnowledgeBase {
128
128
  // Ensure we meet weekly quota
129
129
  await this.ensureWeeklyQuota()
130
130
 
131
+ // Update statistics
132
+ this.updateStatistics()
133
+
131
134
  await this.saveKnowledgeBase()
132
135
 
133
136
  return {
@@ -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
- let inFunction = false
510
- let functionStartLine = 0
507
+ // Track nested structures by counting braces and if statements
511
508
  let braceDepth = 0
512
- let decisionPoints = 0
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 start
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
- decisionPoints = 1 // Start with 1 for the function itself
524
+ ifDepth = 0
525
+ maxNestingDepth = 0
535
526
  }
536
527
 
537
528
  if (inFunction) {
538
- // Count braces to track function scope
539
- for (const char of line) {
540
- if (char === '{') braceDepth++
541
- else if (char === '}') braceDepth--
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
- // Count decision points
545
- if (trimmed.includes('if ') || trimmed.includes('else if')) {
546
- decisionPoints++
547
- } else if (trimmed.includes('for ') || trimmed.includes('while ')) {
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
- // Check for function end (when we exit the function's brace scope)
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: `Cyclomatic complexity: ${decisionPoints} (recommended: < 10)`,
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
- // Track brace depth
624
- for (const char of line) {
625
- if (char === '{') {
626
- braceDepth++
627
- inBlock = true
628
- } else if (char === '}') {
629
- braceDepth--
630
- if (braceDepth === 0) {
631
- inBlock = false
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
- if (!inBlock && (trimmed.startsWith('return') || trimmed.startsWith('throw') ||
650
- trimmed.startsWith('exit') || trimmed.startsWith('sys.exit'))) {
651
- foundReturn = true
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
- if (trimmed.includes('api_key') || trimmed.includes('password') ||
727
- trimmed.includes('secret') || trimmed.includes('token')) {
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
- // Single letter variables (except loop counters)
799
- const singleLetterVar = line.match(/\b([a-z])\b(?!\s*[=+\-*/]|\s*[;,\)\]])/g)
800
- if (singleLetterVar && singleLetterVar.length > 0) {
801
- // Check if it's not a common loop counter
802
- const hasLoopCounter = /\bfor\s*\(\s*[a-z]\s+/.test(line)
803
- if (!hasLoopCounter) {
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,
@@ -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: trend.values[trend.values.length - 1] || 0,
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[trend.dates.length - 1]
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 > trend.values[trend.values.length - 1] ? 'up' : 'down'
270
+ direction: predicted > lastValue ? 'up' : 'down'
263
271
  }
264
272
  }
265
273
 
@@ -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(`PRD not found at ${this.config.prdPath}`)
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
- console.log(chalk.yellow('Ralph loop already running'))
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.toLowerCase()}`
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 loads correctly
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-simplifier",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Code-Simplifier持续改进系统 - 自动化的代码质量监控、持续改进和知识管理工具(支持 ESLint、Git 钩子、自动修复、多语言分析、完整测试套件、AI驱动分析)",
5
5
  "keywords": [
6
6
  "code-quality",