Repository navigation
Releases: O6lvl4/codopsy
Release list
v2.3.1
Almide: module-qualified patterns parse
tree-sitter-almide is now at v0.3.1 (was v0.3.0). It reads two more shapes of current Almide:
- module-qualified names in patterns:
core.Stop,finish.Done(x),wire.Folded { content, .. }as match arms if letexpressions
Before this, a match arm on a module-qualified pattern was a parse error, and the function around it was measured from a partly unread tree. (#10)
v2.3.0
Unreadable files no longer pass as clean
A file the grammar could not read produced no functions and no issues, so it scored close to a perfect A and counted as a clean file in the grade distribution. A Go file that was 85.6% unparsed scored A (99).
- A file with more than 20% of its bytes unparsed is now unanalyzed: it gets no score and is left out of the weighted average and the grade distribution. The summary counts these files, and
-vshows what share of each one was unparsed. (#4) --fail-on-unanalyzedmakes the run exit 1 when any file is unanalyzed, so CI can refuse code the tool could not read. It is off by default. (#5)
Almide: current syntax parses
tree-sitter-almide is now at v0.3.0 (was v0.1.0). It reads the Almide being written today:
- types:
T?, module-qualified types,-> T!/-> T!E - expressions:
fan { a, b }and thefan.*heads - strings: escaped quotes and
//inside them
Before this, 18 of 39 files across three real Almide projects came back unanalyzed. All 39 are read now. (#9)
Go 1.27
- Generic methods parse, through a tree-sitter-go fork pinned until the upstream change lands (#8 tracks going back to crates.io). (#6)
- New rule
go-generic-method: whengo.moddeclares a version below 1.27, generic methods are reported, asgo vet's stdversion check would. (#7)
Fixes
max-depthcounted everyelse ifas one level deeper. A long else-if chain reported "nesting depth 5" where the code nests once. Anelse ifnow stays at the depth of theifit continues, as ESLint's max-depth counts it. This affects every language with else-if chains. On one Almide project, warnings went from 85 to 15, and all 15 that remain are real. (#9)- Almide
else ifchains stay flat in cognitive complexity, as JS, Rust and Lean chains already did. (#9) unused-importignored uses in TypeScript type positions, so an import used only as a type was reported unused. On one app, 82 of 83 reports were this false positive. (5626620)
This changes reported numbers. Scores go down where unreadable files used to pass as clean. They go up where long else-if chains were reported as deep nesting. A baseline recorded with 2.2.0 will show both kinds of change.
v2.2.0
Lean 4 support
codopsy now analyses Lean 4 (.lean) — 35 languages, 182 lint rules.
- Cyclomatic and cognitive complexity for
def/theorem/abbrev/example/instance, scored against Lean's own control flow:if then else,matcharms,induction,by_cases, and the∧/∨connectives.else ifchains stay flat instead of nesting. - 8 lint rules aimed at the two things that make a Lean file untrustworthy rather than merely ugly — proofs that are not finished, and declarations that widen what the kernel takes on faith:
| Rule | Default |
|---|---|
no-sorry |
error |
no-axiom |
warning |
no-native-decide |
warning |
no-unsafe |
warning |
no-unlimited-heartbeats |
warning |
no-partial-def |
info |
no-dbg-trace |
info |
no-debug-command |
info |
Lean is macro-extensible, so no tree-sitter grammar reads every file. Measured over lean4 and Mathlib core sources, the grammar codopsy uses leaves ~6% of bytes unparsed; hand-written Lean parses essentially in full. What it cannot read is now reported rather than silently skipped — see the next section.
New rule: syntax-error (all languages, info)
A file the parser could not read produced no functions, no issues, and therefore a perfect score. It now reports how many regions were unreadable and what share of the file they cover, so an unparsed file can no longer pass as a clean one. One issue per file; disable it with "syntax-error": false like any other rule.
Complexity fix: keyword tokens were counted twice
Several tree-sitter grammars name a keyword token exactly like the node it opens — Ruby's if token inside an if node, JavaScript's function inside a function_declaration. Both matched the classification tables, so every such branch was counted twice and phantom (anonymous) functions appeared in reports. Only named nodes are classified now.
This changes reported numbers for Ruby, Crystal, JavaScript and Lua: complexity and function counts go down. A baseline recorded with 2.0.x will register the difference as an improvement.
Other fixes
--hotspotssilently produced nothing unless the analysed directory was the repository root. Churn keys fromgit logare repo-root-relative; the comparison was built relative to the analysed directory, so the two never matched in a subdirectory.--helpstill described codopsy as being "for TypeScript, JavaScript & Rust".--max-complexity/--max-cognitive-complexityCLI defaults were hardcoded instead of reading the centraliseddefaults, so they could drift from the documented values.score_complexitynow honours the configuredmax-complexity/max-cognitive-complexitythresholds instead of the built-in ones (this landed after v2.0.1 and was never released).
Internal
The language registry is a single table per language, replacing three lists that had to stay in lockstep: a name array indexed by enum declaration order, a grammar match, and an extension map. Adding a language is now one row, and a new test asserts that all 35 grammars actually load and that every extension round-trips.
codopsy's own analysis of src is A (100/100), 0 issues.
Full Changelog: v2.0.1...v2.2.0
v2.0.1
v1.2.0 - Almide Support
What's New
- Almide language support — 25th language added via
tree-sitter-almidegrammar - Almide-specific AST handling:
function_namenode,test_declaration,for_in_expression,do_expression - Keyword logical operators (
and/or) recognized for cognitive complexity scoring
Full language list: TypeScript, TSX, JavaScript, Rust, Go, Python, C, C++, Java, Ruby, C#, PHP, Scala, Haskell, Bash, HTML, CSS, JSON, OCaml, Swift, Lua, Zig, Elixir, YAML, Almide
codopsy v1.1.0
What's New
Added 5 new languages:
- Swift (
.swift) - Lua (
.lua) - Zig (
.zig) - Elixir (
.ex,.exs) - YAML (
.yml,.yaml)
Total: 24 languages supported.
Note: Kotlin and Dart are not yet supported due to tree-sitter grammar ABI incompatibility. Will be added when their crates are updated.
Install / Update
cargo install --git https://github.com/O6lvl4/codopsy.gitcodopsy v1.0.0
codopsy v1.0.0
AST-level code quality analyzer for 19 languages.
Supported Languages
TypeScript, TSX, JavaScript, Rust, Go, Python, C, C++, Java, Ruby, C#, PHP, Scala, Haskell, Bash, HTML, CSS, JSON, OCaml
Features
- Cyclomatic & cognitive complexity analysis per function
- Language-specific lint rules — JS/TS: 11 rules, Rust: 6 rules
- Threshold rules for all languages (max-lines, max-depth, max-params, max-complexity)
- A–F quality scoring (complexity 35% + issues 40% + structure 25%)
- Baseline tracking for quality degradation detection
- Hotspot detection — complexity × git churn
- Parallel analysis via rayon
- Configurable via
.codopsyrc.json
Install
cargo install --git https://github.com/O6lvl4/codopsy.gitQuick Start
codopsy analyze ./src # Analyze a project
codopsy analyze ./src -v # Verbose per-file output
codopsy analyze ./src --hotspots # Show complexity hotspots
codopsy init # Generate .codopsyrc.json