pi-lens 1.3.8 → 1.3.10

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.
@@ -212,7 +212,7 @@ export class ComplexityClient {
212
212
  maintainabilityIndex: Math.round(maintainabilityIndex * 10) / 10,
213
213
  linesOfCode: codeLines,
214
214
  commentLines,
215
- codeEntropy: Math.round(codeEntropy * 1000) / 1000,
215
+ codeEntropy: Math.round(codeEntropy * 100) / 100,
216
216
  };
217
217
  } catch (err: any) {
218
218
  this.log(`Analysis error for ${filePath}: ${err.message}`);
@@ -247,11 +247,9 @@ export class ComplexityClient {
247
247
  parts.push(` Max nesting: ${metrics.maxNestingDepth} levels (consider extracting)`);
248
248
  }
249
249
 
250
- // Code entropy (0-1, lower = more predictable)
251
- if (metrics.codeEntropy > 0.8) {
252
- parts.push(` Entropy: ${metrics.codeEntropy} (highcode may be unpredictable/AI-generated)`);
253
- } else if (metrics.codeEntropy < 0.3) {
254
- parts.push(` Entropy: ${metrics.codeEntropy} (low — repetitive patterns detected)`);
250
+ // Code entropy (in bits, >3.5 = risky AI-induced complexity)
251
+ if (metrics.codeEntropy > 3.5) {
252
+ parts.push(` Entropy: ${metrics.codeEntropy.toFixed(1)} bits (>3.5risky AI-induced complexity)`);
255
253
  }
256
254
 
257
255
  // Function length
@@ -267,6 +265,35 @@ export class ComplexityClient {
267
265
  return parts.length > 0 ? `[Complexity] ${metrics.filePath}\n${parts.join("\n")}` : "";
268
266
  }
269
267
 
268
+ /**
269
+ * Check thresholds and return actionable warnings
270
+ */
271
+ checkThresholds(metrics: FileComplexity): string[] {
272
+ const warnings: string[] = [];
273
+
274
+ if (metrics.maintainabilityIndex < 60) {
275
+ warnings.push(`Maintainability dropped to ${metrics.maintainabilityIndex} — extract logic into helper functions`);
276
+ }
277
+
278
+ if (metrics.cyclomaticComplexity > 10) {
279
+ warnings.push(`High complexity (${metrics.cyclomaticComplexity}) — use early returns or switch expressions`);
280
+ }
281
+
282
+ if (metrics.cognitiveComplexity > 15) {
283
+ warnings.push(`Cognitive complexity (${metrics.cognitiveComplexity}) — simplify logic flow`);
284
+ }
285
+
286
+ if (metrics.maxNestingDepth > 4) {
287
+ warnings.push(`Deep nesting (${metrics.maxNestingDepth} levels) — extract nested logic into separate functions`);
288
+ }
289
+
290
+ if (metrics.codeEntropy > 3.5) {
291
+ warnings.push(`High entropy (${metrics.codeEntropy.toFixed(1)} bits) — follow project conventions`);
292
+ }
293
+
294
+ return warnings;
295
+ }
296
+
270
297
  /**
271
298
  * Format delta for session summary
272
299
  */
@@ -504,9 +531,9 @@ export class ComplexityClient {
504
531
  }
505
532
 
506
533
  /**
507
- * Calculate Shannon entropy of code tokens
508
- * Measures predictability/uniformity of code
509
- * 0 = completely uniform, 1 = maximum entropy
534
+ * Calculate Shannon entropy of code tokens (in bits)
535
+ * Uses log2 for entropy measured in bits
536
+ * Threshold: >3.5 bits indicates risky AI-induced complexity
510
537
  */
511
538
  private calculateCodeEntropy(sourceText: string): number {
512
539
  // Tokenize by splitting on whitespace and common delimiters
@@ -526,7 +553,7 @@ export class ComplexityClient {
526
553
  freq.set(token, (freq.get(token) || 0) + 1);
527
554
  }
528
555
 
529
- // Calculate Shannon entropy: H = -sum(p * log2(p))
556
+ // Calculate Shannon entropy in bits: H = -sum(p * log2(p))
530
557
  let entropy = 0;
531
558
  for (const count of freq.values()) {
532
559
  const p = count / tokens.length;
@@ -535,11 +562,7 @@ export class ComplexityClient {
535
562
  }
536
563
  }
537
564
 
538
- // Normalize to 0-1 range
539
- const maxEntropy = Math.log2(freq.size);
540
- if (maxEntropy === 0) return 0;
541
-
542
- return Math.min(1, entropy / maxEntropy);
565
+ return entropy; // Return in bits, not normalized
543
566
  }
544
567
 
545
568
  private isKeyword(text: string): boolean {
package/index.ts CHANGED
@@ -829,6 +829,21 @@ export default function (pi: ExtensionAPI) {
829
829
  }
830
830
  }
831
831
 
832
+ // Complexity threshold warnings (actionable)
833
+ if (complexityClient.isSupportedFile(filePath)) {
834
+ const metrics = complexityClient.analyzeFile(filePath);
835
+ if (metrics) {
836
+ const warnings = complexityClient.checkThresholds(metrics);
837
+ if (warnings.length > 0) {
838
+ let warningReport = `[Complexity Warnings]\n`;
839
+ for (const w of warnings) {
840
+ warningReport += ` ⚠ ${w}\n`;
841
+ }
842
+ lspOutput += `\n\n${warningReport}`;
843
+ }
844
+ }
845
+ }
846
+
832
847
  // Test runner — run tests for the edited file
833
848
  if (!pi.getFlag("no-tests")) {
834
849
  const cwd = process.cwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "Real-time code feedback for pi — TypeScript LSP, Biome, ast-grep, Ruff, TODO scanner, dead code, duplicate detection, type coverage",
5
5
  "repository": {
6
6
  "type": "git",