energy-state-analyzer 0.6.0 → 0.7.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.
@@ -1,9 +1,11 @@
1
1
  # File Coherence
2
2
 
3
- Flags files that have lost a single responsibility, the "utils/helpers sprawl" pattern, along three independent signals.
3
+ Flags files that have lost a single responsibility, the "utils/helpers sprawl" pattern, along four independent signals.
4
4
 
5
5
  ## What it flags
6
6
 
7
+ **Class methods are grouped by their enclosing class, not counted as free-standing functions.** Every signal below that operates on "functions" (function-count sprawl and its type-cohesion check) only ever sees module-level, free-standing functions — a class's methods are excluded and evaluated instead by the class-relatedness check further down. This matters for OOP-style files (Python/TypeScript/Kotlin; F# has no class construct this detector models): without it, a file of two small, tightly related classes with 15 methods total between them reads exactly like 15 unrelated free-standing functions, and the type-cohesion signal — which only sees each method's own explicitly-annotated parameter/return types, not the fact that the method belongs to one of only two classes — misreads the handful of incidental types (mostly the classes' own names, showing up in constructors and factory-style methods) as "unrelated types" sprawl. A class's own method count isn't this detector's concern at all (that would be a separate large-class/"god class" detector, not implemented here).
8
+
7
9
  **Function-count sprawl.** A file with more than 12 functions is flagged (medium; high past 15). The threshold drops to 8 if the filename itself contains `util`, `helper`, or `common`, treating the name as a proxy for "already known to be a grab-bag." The violation message deliberately steers away from "just split this file" — since the detector only sees one file, it can't tell whether a split would land the code in an existing cohesive module or just distribute the same coupling across new files with more imports wiring them back together.
8
10
 
9
11
  A file is exempted from this check regardless of count if either of two independent cohesion signals says otherwise (unless the filename already admits to being utils/helper/common, which overrides both):
@@ -17,6 +19,17 @@ If a file is still going to be flagged and its type signal is confidently measur
17
19
 
18
20
  **Import sprawl.** A file drawing from more than 10 distinct modules/packages is flagged (medium; high past 15). Counts distinct import *sources*, not raw import lines/symbols, since that's not comparable across languages: TS (`import { a, b, c } from 'x'`) and Python (`from x import a, b, c`) can bundle many symbols from one module into a single import line, but Kotlin has no equivalent grouping syntax and idiomatic style (ktlint's `no-wildcard-imports`) forbids collapsing them with `import x.*`, so each symbol needs its own line. Counting raw lines would flag a Kotlin file pulling many symbols from a handful of packages as far more sprawling than an equivalent TS/Python file with identical actual coupling.
19
21
 
22
+ **Class relatedness (Python/TypeScript/Kotlin only).** A file containing 2 or more classes is flagged when those classes split into multiple families with nothing connecting them — no shared inheritance, no method signature referencing another class in the file, and no shared naming affix. Unlike the checks above, there's no minimum class count before this can fire: a class is already a much stronger unit of cohesion than a single function (a whole type, not one operation), so even 2 completely unrelated classes are worth flagging.
23
+
24
+ Two classes are linked into the same family by any of:
25
+ - *Direct inheritance* - one class's base name is the other's own name.
26
+ - *Shared base* - both classes extend/implement the same name, even one not defined in this file at all (e.g. a whole `exceptions.py`-style file of classes that all extend `Exception` but never reference each other).
27
+ - *Type cross-reference* - a method's signature (parameter or return type) names another class defined in the file, as with a token/token-source pair where one constructs or returns the other.
28
+
29
+ If the resulting graph still splits into more than one family, a naming-affix fallback (shared prefix or suffix across class names, e.g. `FooError`/`BarError`) gets one last chance to treat the whole file as one family before it's flagged. Unlike the function-count check's type-diversity signal, an unconnected class graph is only an absence of positive evidence, not a positive measurement of diversity - so it isn't treated as authoritative over naming.
30
+
31
+ This check says nothing about a single class carrying too much responsibility by itself (a "god class") - that's a distinct concern, deliberately not covered here.
32
+
20
33
  ## Example
21
34
 
22
35
  ```python
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "energy-state-analyzer",
3
3
  "displayName": "Energy State Analyzer",
4
4
  "description": "Visualize code energy states in real-time (Python, F#, TypeScript, Kotlin)",
5
- "version": "0.6.0",
5
+ "version": "0.7.0",
6
6
  "publisher": "cardamom-code",
7
7
  "license": "MIT",
8
8
  "icon": "images/icon.png",
@@ -161,6 +161,11 @@
161
161
  ],
162
162
  "description": "String literals that are never flagged as magic strings, regardless of context."
163
163
  },
164
+ "energyStateAnalyzer.includeFixtures": {
165
+ "type": "boolean",
166
+ "default": false,
167
+ "description": "Analyze files that .esaignore excludes when opened in the editor, for visually verifying detector fixtures. Does not affect the CLI/CI scan, which always honors .esaignore."
168
+ },
164
169
  "energyStateAnalyzer.matchOpportunity.minBranches": {
165
170
  "type": "number",
166
171
  "default": 3,