Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[1.20.0] - 2026-06-05
🎉 First minor since v1.0 — and the first release with a proper home on the web. https://cha.to01.icu is now a real product page, not just a README dump. Star the repo, share the link, and tell your linter Cha said hi.
The analyzer itself didn't move — every --version-visible behavior is identical to v1.19.0. What landed is the documentation, presentation, and integration story: somewhere we can actually point new users.
Added
- Bilingual documentation site at https://cha.to01.icu — landing page (oranda) plus a full mdbook tree at
/book/(English) and/book/zh-CN/(中文). 60+ pages covering install, quick-start, every CLI subcommand, every output format, LSP integration for VS Code / Helix / Neovim / Zed, configuration reference, JSON Schema, plugin development, six worked recipes (migrate from clippy, monorepo CI, suppress in legacy code, custom plugin in 50 lines, calibrate to your codebase, baseline workflow), three contributor guides (architecture, writing a smell, releasing), and an academic-references page tracing every smell back to its source. - Cookbook recipes — six task-oriented walkthroughs:
migrate-from-clippy,monorepo-ci,suppress-legacy,custom-plugin-50loc,calibrate,baseline. Each starts with a problem statement and ends with copy-pasteable commands. Authored in English and Chinese (the Chinese versions are written natively, not translated, so the prose style matches the rest of zh-CN). - Contributor guides —
contributing/architecture.mddocuments the seven-crate workspace and data flow with mermaid diagrams;writing-a-smell.mdwalks throughMiddleManAnalyzeras the worked example;releasing.mdis the runbook forcargo xtask bump→release. - Configuration reference — every
.cha.tomlkey, grouped by plugin, with thresholds and defaults sourced fromcha-core/src/plugins/. JSON Schema reference page documentingcha schemaoutput and how to wire it into IDEs. - Bilingual landing page (
LANDING.md) — hero with logo + tagline + three CTAs, six feature cards (detectors, WASM SDK, LSP, git-aware analysis, output formats, two-level cache), 30-second get-started block, smell-category table, and editor integration links. Replaces the previous "README dumped into the homepage" experience. - CJK-aware search via Pagefind 1.4 — replaces mdbook's bundled elasticlunr (which silently dropped non-ASCII tokens, leaving the zh-CN tree un-searchable). Press
sor/on any docs page to open a modal that indexes both language trees. - Per-page social meta + branded OG card — every page emits
og:title/og:image/twitter:cardso Slack / iMessage / 微信 link previews show a real card instead of a 32×32 favicon. The 1200×628 card embeds the Cha logo and is regenerable viapython3 static/gen_og_card.py. - Custom 404 page — site-wide warm-themed 404 with the Cha logo and shortcuts back into the live parts of the site, replacing GitHub Pages' generic gray 404.
- Language switcher in mdbook header — the toolbar globe icon switches between EN and zh-CN trees and remembers per-page equivalents.
xtask docs-check— verifies every page referenced inbook/src(-zh-CN)/SUMMARY.mdactually exists; runs as part ofcargo xtask ciso a broken SUMMARY can't ship.xtask i18n-check— flags zh-CN pages whose git ctime trails their English counterpart, surfacing translation drift.xtask docgen-cli— generatesbook/src/reference/cli-manual.mdfromcha help-markdownso the CLI manual page in docs always matches the binary's actual--help. Hiddencha help-markdownsubcommand added for this.- VS Code extension page — full inventory of what each LSP capability looks like inside VS Code (wavy underlines, lightbulbs, code-lens overlays, inlay hints, hover cards, semantic-token modifier, status-bar workspace scan progress) plus the
cha.disabledPluginssetting documentation.
Fixed
- tree-sitter S-expression query link — the link in
docs/plugin-development.mdand the zh-CN translation pointed at/syntax-highlighting/queries, which 404s on the current tree-sitter docs site. Updated to/using-parsers/queries/. docs/plugin-development.mdFunctionInfo/ClassInfofield tables — were bare struct dumps; now per-field semantic tables (type + what each field actually drives) for both languages.- README plugin table — every entry now has an anchor link into
docs/plugins.md's detailed description, in bothREADME.mdandREADME.zh-CN.md. The previousUnstableDependencyrow (which never matched a real detector) was removed andasync_callback_leakadded.
Changed
- Project homepage —
https://cha.to01.icuis the canonical entry point. README still works on github.com but now points at the docs site for anything beyond the quick-start. - CI — the
Webworkflow builds the EN tree via oranda, re-builds the zh-CN tree via mdbook withMDBOOK_BOOK__SRC=src-zh-CN, indexes both with Pagefind, and restorespublic/CNAME+ dropspublic/404.htmlafter oranda's clean. Deploys viaJamesIves/github-pages-deploy-action@v4.6.4.
Notes for upgraders
Nothing to do. cha analyze produces the same output, .cha.toml accepts the same keys, every CLI flag still works. This is a documentation release: the binary moves from "had a single README" to "had a 60-page bilingual docs site" without changing anything you'd notice from the terminal.
If you want to celebrate by reading something other than --help: https://cha.to01.icu.
[1.19.0] - 2026-05-22
Hardcoded thresholds and keyword lists become plugin config; cha fix stops hardcoding smell names; the last two text-scanning detectors switch to AST queries.
Added
Plugin::try_fix(finding, ctx) -> Option<Patch>— every plugin can now contribute auto-fixes.cha fixwalks all enabled plugins and asks each one. Adding fix support for a new smell is one trait override, not a host-sideif smell_name == ...patch.cha_core::Patch/cha_core::TextEdit— public byte-range edit types for plugin authors. Edits within a single finding are applied in reverse byte-offset order.DesignPatternAdvisorconfig — 8 magic-number thresholds (strategy_min_arms,state_min_arms,builder_min_params,builder_alt_min_params,builder_alt_min_optional,null_object_min_count,template_min_self_calls,template_min_methods) and 2 keyword lists (type_field_keywords,state_field_keywords) are now overridable via[plugins.design_pattern].GodClassAnalyzer::min_tcc— Tight Class Cohesion threshold (Lanza-Marinescu's 1/3) is configurable.cache.rswalker skip-dirs — extended from{target, node_modules, dist}to also skipbuild,out,__pycache__,venv,.venv,vendor. Reduces unnecessary.cha.tomldiscovery work in polyglot repos.
Fixed
switch_statement/message_chain: replaced bespoke text scanners (find_switch_keyword,walk_chainand ~140 lines of hand-rolled tokenization) with tree-sitter queries againstswitch_statement/match_expression/field_expression/member_expression/selector_expression. Smell counts unchanged, but keyword positions are now sourced from the AST. No more false positives on keywords inside strings.inappropriate_intimacyimport resolution: extension probe expanded from{.ts, .tsx, .rs}to also include.py,.go,.cpp,.cc,.cxx,.c,.h,.hpp,.hxx,.js,.jsx,.mts,.cts. Sibling-file lookups in non-JS/Rust projects no longer silently fail.calibrate.rstable rendering: 3 metric labels were hardcoded across 3 separateprintlnblocks. Adding a calibration metric now means one entry in a(label, samples)array.
Changed
cha fixdelegates toPlugin::try_fixfor every finding — no morefilter(|f| f.smell_name == "naming_convention")on the host. Existing behavior preserved (NamingAnalyzer fixesnaming_conventionPascalCase violations); other plugins returnNoneuntil they opt in.
[1.18.0] - 2026-05-22
Built-in detectors now use AST queries instead of text scanning. Several core plugins previously did substring matches that misfired on strings, comments, and unrelated identifiers.
Added
cha_core::query— host-side tree-sitter query helper (run_query/run_queries/node_to_match). Both built-in plugins and the WASMtree_queryhost import now go through this single API.DeadCodeAnalyzer::entry_points— entry-point names are now configurable via[plugins.dead_code] entry_points = [...]. Default list expanded from Rust-only (5 names) to multi-language (Rust + Python__init__etc + Goinit+ C_start+ tokio).LengthAnalyzer::complexity_factor_threshold— was hardcoded10.0, now configurable via[plugins.length].
Fixed
unsafe_api: rewritten from line-basedline.contains+ odd-quote-count heuristic to per-language tree-sitter queries. Picks up realsprintf/strcpy/strcat/systemcall sites that the line-based heuristic missed. Comments and string literals containing keywords likeunsafeno longer false-positive.dead_code: substringis_in_file_referencedreplaced with AST identifier scan. Token-concat macro detection rewritten — instead of nuking the entire file when any#define ... ##exists, parse define bodies forprefix##X##suffixslots, scan call sites for invocation arguments, synthesize plausible expansion names, and add them to the reference set. X-macro dispatch tables (e.g.STYLE_DEF) no longer hide every dispatch function.IdentifierPositionslookup is now O(1) per symbol viaHashMap<name, Vec<line>>.error_handling:unwrap_abuseuses tree-sitter ((call_expression field_expression unwrap|expect)); empty-catch detection is per-language (Rust skipped, TScatch_clause, Pythonexcept_clause). String literals and comments containing the substringunwraporcatchno longer trigger.hardcoded_secret: regex matches now run againststring_literalnode text only, not full source lines. Comments and identifier names with secret-like substrings no longer false-positive.cha fix:String::replacewhole-content substitution replaced with tree-sitter identifier-node range collection + byte-offset reverse substitution. The previous implementation could rewrite identifier names inside string literals and comments, corrupting source files.git_metrics::check_test_ratio:f.contains("test") || f.contains("spec")replaced withcha_core::is_test_path. The substring check wrongly countedrequest.rs/spectrum.rsetc. as test files, polluting the test-to-production ratio that driveslow_test_ratio.wasm.rs::infer_file_role: replaced duplicate test-path heuristics withcha_core::is_test_path. WASM plugins'FileRole::Testclassification now matches the canonical convention used elsewhere (__tests__/,__mocks__/,.test.ts,.spec.ts).find_macro_invocation_args: word-boundary check added —STYLE_DEFno longer matchesSTYLE_DEFINEinvocations.
Removed
unsafe_apiis_in_stringheuristic — superseded by tree-sitter queries that distinguish string literals at the AST level.error_handlingline-baseddetect_empty_catch— replaced with grammar-aware queries.HostState::query_cache— query compilation now lives incha_core::query(compile-on-demand; LRU caching to be added if measurement warrants).
[1.17.0] - 2026-05-21
Added
project_query::function_at(path, line, col)— new host import returning theFunctionInfowhose body contains the given position. Useful for tree-query–driven detectors that need to disambiguate which declared function a queried position belongs to.WasmPluginTest::option_list / option_bool / option_int / option_float— list and typed option setters in the test harness, replacing the previous string-onlyoption().
Changed (breaking for WASM plugins)
tree_query::QueryMatch.start_line/end_lineare now 1-based (was 0-based). Aligns withFunctionInfo/ClassInfo/CommentInfoline numbering — no more per-plugin off-by-one conversion. Inputs tonode_at(line, col)andnodes_in_range(start, end)are likewise 1-based now.- Existing plugins compiled against the pre-1.17 WIT will need to be rebuilt against the new SDK; instantiation will fail loudly otherwise.
Fixed
react-hooksexample plugin — false positives onhook_after_early_return(in sibling components and inside return expressions likereturn useState()) eliminated by switching toproject_query::function_atfor host-function disambiguation. Now reports 5 true positives / 0 false positives on the 6-component .tsx fixture (was 5 / 2).
Documentation
docs/plugin-development.md: added Line/Column convention note, Project Query API section, WASM Compatibility Cheatsheet (regex panics, no clock, no FS),cha plugin buildvscargo builddistinction, and new option helpers in Testing.
[1.16.0] - 2026-05-21
Added
TsxParserincha-parser—.tsxfiles now route to a parser usingtree_sitter_typescript::LANGUAGE_TSX, so JSX nodes (jsx_element,jsx_attribute,jsx_self_closing_element) are first-class AST citizens. WASM plugins can now match them viatree_query::run_query.examples/wasm-plugin-react-hooks— example WASM plugin demonstratingtree_queryintegration. Detects 5 React Rules of Hooks violations: hooks called from non-component functions, hooks in conditionals, hooks in loops, hooks after early return, and hooks in nested callbacks.examples/wasm-plugin-todo-tracker— example WASM plugin demonstrating extended TODO comment tracking beyond the builtintodo_tracker. Adds 5 new smells: extended tag set (BUG/WIP/OPTIMIZE/PERF/DEPRECATED + user-configurable extras),(by:YYYY-MM-DD)expiration, priority escalation (!/!!/!!!), per-file TODO hotspot detection, and required-attribution policy.
Notes
- WIT unchanged at
cha:plugin@0.3.0(no breaking change). - Routing for
.ts/.mts/.ctscontinues to useLANGUAGE_TYPESCRIPT. Only.tsxswitched.
[1.15.0] - 2026-05-14
Added
ProjectQuerytrait incha-core— plugins now access cross-file data through a typed interface onAnalysisContext.projectinstead of host-side post-hoc string-matched filtering. 12 methods cover the project-level queries existing post-analysis passes need:is_called_externally,callers_of,function_home/function_by_name/class_home,is_third_party,workspace_crate_names,is_test_path, etc. WASM plugins also gain access via theproject-queryhost import.ProjectQueryBulktrait extendsProjectQueryfor in-process iteration (iter_models); not exposed to WASM.cha_core::is_test_path— public utility consolidating two duplicated implementations.- example-wasm
unused_helpersmell — demonstratesproject_query::callers_ofcallback.
Changed
- WIT bumped to
cha:plugin@0.3.0(breaking) — addsproject-queryhost import. External plugins compiled against0.2.0must rebuild. large_api_surfaceC/C++ heuristics —.h/.hppheaders are now skipped (their 100% public surface is by design);.c/.cppimplementation files use a higher count threshold (30, configurable asc_max_exported_count) and the ratio gate is effectively off (configurable asc_max_exported_ratio). lvgl baseline: 393 → 34 findings (-91%).dead_codeis now project-aware — usesProjectQuery::is_called_externallyto confirm cross-file usage; the per-file text search is just an early shortcut. The token-concat macro heuristic (#define ... ##) remains because parsers don't macro-expand. lvgl baseline: 67 → 6 findings (-91%).
Removed
Plugin::cross_file_aware_smellstrait method — replaced by typed query throughAnalysisContext.project.cha-cli::cross_file_filtermodule — the post-hoc string-matched filter is gone; plugins produce final findings using the typed trait.- 3 duplicated
workspace_crate_namesimpls + 3 duplicatedis_third_party/is_external_leakimpls + 2 duplicatedis_test_pathimpls — all consolidated.
Added
- New
.cha.tomlconfig keys forapi_surface:max_exported_ratio,c_max_exported_count,c_max_exported_ratio,skip_c_headers. All language-aware defaults preserved.
[1.14.0] - 2026-05-14
Added
- Plugin AST Query API — WASM plugins can now execute tree-sitter queries against the current file's AST via the
tree-queryhost import interface (run-query,run-queries,node-at,nodes-in-range). Enables plugins to do custom structural pattern matching without reimplementing parsing. file-roleenum inanalysis-input— host infers whether a file issource,test,doc,config, orgeneratedfrom its path, allowing plugins to apply differential detection strategies.- SourceModel enrichment —
analysis-inputnow includescomments,type-aliases,parameter-names,switch-arm-values, andis-module-declfields previously only available to internal plugins. parse_file_full()incha-parser— returnsParseResultcarrying model + tree-sitterTree+Languagefor downstream use by WASM host callbacks.
Changed
- WIT bumped to
cha:plugin@0.2.0— breaking change: plugins compiled against0.1.0must be recompiled. No behavioral change for existing internal plugins.
[1.13.1] - 2026-04-30
Added
abstraction_leak_surgerydetector — files that co-change in git history and share a third-party type in their function signatures. Upgrade of the classicshotgun_surgery: instead of "these files always change together" (agnostic of why), this pinpoints "these files always change together because they all depend on the same external type" — the shared external type is the concrete abstraction leak driving the co-change cascade. SeverityHint.- Inputs: git co-change counts (
git log --name-only -N, threshold ≥ 5 commits in last 100) × per-fileTypeOrigin::Externalsets derived from parameter / return types. Workspace-sibling crates auto-whitelisted (same mechanismcross_boundary_chain/leaky_public_signatureuse), socha_core-internal dependencies betweencha-parser/cha-clidon't fire. - Cha self-baseline: 10 genuine findings, all pointing at the 5 language parsers sharing
tree_sitter::Node— exactly the abstraction leak the detector is designed to find (tree-sitter upgrades ripple across every parser file). lvglsrc/: 0 (C project, no External origins).
- Inputs: git co-change counts (
[1.13.0] - 2026-04-30
Added
primitive_representationdetector (roadmap S8.2). Flags function parameters whose name carries a domain concept (user_id,email,status_code,api_url,password,language, …) but whose type is a raw scalar primitive (String,i32,bool,char, …). Signals an opportunity to introduce a newtype / value object to preserve the invariant. Per-parameter detection groups all offending params of one function into a single hint. Complements the existingprimitive_obsession(which looks at per-function ratio): this fires on even a single param when it's clearly a business concept.- Business-token and noise-token vocabularies are deliberately narrow to keep signal-to-noise high. Substring matches are ruled out (tokens must be standalone words —
widget_identifierdoes not trigger onid). - Parameters already typed with project-local newtypes (e.g.
id: UserIdwhereUserIdisTypeOrigin::Local) are skipped — the author already did the right thing. - Container types (
Path,PathBuf,Vec,Arc,Box,HashMap, …) are treated as domain-carrying and excluded; wrappingpath: &Pathin a newtype would destroy the abstraction. - Only runs on
is_exportedfunctions — private helpers are noise for a design signal aimed at public API hygiene. - Cha self-analyze: 14 findings (all genuine —
rel_path/env_hash/language/key/hashas raw types). lvglsrc/baseline: 53 findings (TTFplatformID/encodingID/languageID/nameID: int, file-explorerpath/dir: charpointers, …).
- Business-token and noise-token vocabularies are deliberately narrow to keep signal-to-noise high. Substring matches are ruled out (tokens must be standalone words —
stringly_typed_dispatchdetector (roadmap S8.8). Flags functions whoseswitch/matchbody dispatches on ≥ 3 string or ≥ 3 integer literal arms — classic "the arm values should have been an enum" smell. Char-literal arms (C tokenisers) skipped. Enum-variant / structural-pattern arms classify asOtherand never contribute to the threshold, somatch event { Event::Click => …, Event::Scroll => …, _ => … }stays quiet whilematch s { "click" => …, "scroll" => …, "submit" => … }fires. SeverityHint. Complements S8.2primitive_representation(signature side) with the body-side dispatch signal.- New
cha_core::ArmValueenum (Str / Int / Char / Other) +FunctionInfo.switch_arm_values+FunctionSymbol.switch_arm_values. Populated by every parser via a new sharedcha-parser/src/switch_arms.rshelper — language-specific arm-node kinds funnel through one classifier. - Cha self-baseline: 20 findings (all node-kind dispatchers in the 6 language parsers — valid detections, users can add
// cha:ignore stringly_typed_dispatchif the dispatch shape is forced by tree-sitter). lvglsrc/baseline: 23 findings (PNG/JPEG/QR error-code dispatchers, color-format size tables, TTF bytecode interpreter).
- New
cross_boundary_chaindetector (roadmap S8.U4). Flags functions wherechain_depth ≥ 3and the chain's root parameter is externally-typed (TypeOrigin::External(crate)) — the function is reaching into a third-party library's internal field layout, not just over-chaining local data. Companion to the existingmessage_chain(which fires on depth regardless of source):cross_boundary_chainis narrower but a stronger abstraction-leak signal. SeverityHint.- Workspace crates are auto-whitelisted (same mechanism
leaky_public_signatureuses), so siblingcha_core::Findingtraversals inside this repo don't fire. Cha self-baseline: 4 findings, all genuinetree_sitter::Nodetraversals incha-parser. lvglsrc/baseline: 0 (C project, fewExternalorigins by design). - Zero parser changes — reuses
chain_depth,parameter_types(with origin),parameter_names,external_refs. Pure post-pass onProjectIndex.
- Workspace crates are auto-whitelisted (same mechanism
FunctionInfo.parameter_names+FunctionSymbol.parameter_names(cha-core). Parallel toparameter_types: identifier names in declaration order. All six parsers (Rust / TS / Python / Go / C / C++) extract these;self/ C++thispositions skipped to stay length-aligned withparameter_types. Enables name-semantic analyses likeprimitive_representation, future LSP hover with full signatures, futurecha summary.- New helpers
cha_parser::rust_imports::rust_param_namesandcha_parser::cpp::c_param_nameextract identifier names from their language's declarator chains; reused across all C/C++ function-definition sites.
[1.12.0] - 2026-04-28
Added
SymbolIndex— structural view of a file, cached separately fromSourceModel. New type incha-core::modelcarrying the fields consumers likecha deps, LSP workspace-symbols, and futurecha summaryall share — class/function names + signatures + positions +type_aliases— without per-function-body data (complexity, body hash, TypeRef origin, cognitive, chain depth etc. stay inSourceModel).ProjectCache::{get,put}_symbolsstore tosymbols/{chash}.bin, mirrored independently ofparse/{chash}.bin. Sameenv_hashmechanism invalidates both on parser code changes.cached_symbols(path)is a new warm fast path that skipsSourceModeldeserialisation entirely —symbols/{chash}.binis roughly 10% the size ofparse/{chash}.bin.cached_parsenow populates both caches on every fresh parse, so the two views are always in lockstep.lvgl src/warm benchmarks (379 files):deps --type imports1.28s → 38ms (34×),--type classes1.30s → 56ms (23×),--type calls1.30s → 48ms (27×). Edge counts unchanged vs. pre-migration (1351/142/8109).cha-cli/src/c_oop_enrichgrows aenrich_c_oop_symbols/attribute_methods_by_name_from_symbolspair alongside the existingSourceModelfunctions. Sharedattribute_one_rawkeeps attribution rules single-sourced; build-index / write-back are deliberate parallel code paths because the two storage types have to stay independent.cha-cli/src/parse_cache.rs(new module) hosts bothcached_parseandcached_symbols.
- C++ parser now handles
ClassName::method()out-of-class definitions, namespaces, and templates. Three gaps in the previous CppParser have been closed:void Foo::bar() {...}(and::global(),A::B::c(), destructorsFoo::~Foo(), operatorsFoo::operator+()) was silently dropped —find_func_name_nodeonly accepted bareidentifierdeclarators. It now also unwrapsqualified_identifier,destructor_name, andoperator_name.- Out-of-class method definitions now attribute to their owning same-file class:
void Foo::bar()bumpsClassInfo::method_countonFooand flipshas_behavior. Cross-file attribution still runs throughcha-cli::c_oop_enrich. namespace_definition,linkage_specification(extern "C" { ... }), andtemplate_declarationare now explicitly matched in the top-level dispatch (previously fell through to the generic recursion arm) — same observable behaviour, but the nesting constructs are now a stable hook rather than an accidental default-case artefact.- C++-specific declarator helpers moved to a new
cha-parser/src/cpp.rssoc_lang.rsstays below thelarge_filegate.
SourceModel.type_aliasesnow populated for Rust, TypeScript, Python, and Go (previously all four returned emptyvec![]with parser-side TODOs). Each parser recognises its language's alias form and records(alias, rhs)pairs: Rusttype X = Y;/pub type X<T> = Y;, TypeScripttype X = Y;/export type X<T> = Y;, Python 3.12+type X = Yand pre-3.12X: TypeAlias = Y, Gotype X = Y(only the true alias form —type X Ydefined types are excluded). Plain PythonX = Yassignments remain unclassified (too ambiguous). Shared extraction lives in a newcha-parser/src/type_aliases.rsmodule so per-language files stay below thelarge_filegate.
Changed
boundary_leakdetector migrated toProjectIndex. The three smells it emits (abstraction_boundary_leak,return_type_leak,test_only_type_in_production) previously parsed the whole project a second time — the codebase noted a "cached model occasionally drops typedef aliases" concern with root cause TBD. v1.11.0's binary-mtime cache keying removed the suspected root cause, and a newcache::tests::cache_roundtrip_preserves_type_aliasesunit test makes the invariant a testable one.boundary_leak::detectnow takes&ProjectIndexand shares the same parse pass asanemic_domain_model,typed_intimacy,module_envy, and friends. Verified against lvgl'ssrc/tree: 155 findings before = 155 findings after (abstraction_boundary_leak: 154, return_type_leak: 1). Completes roadmap S8.infra.4.
Fixed
- C++: template specialisation methods attribute to the right class.
template<> void Foo<int>::bar()used to drop on the floor because the qualifierFoo<int>(atemplate_typenode) didn't match the stored class nameFoo.attach_to_classnow strips trailing<...>template arguments before matching, so out-of-class specialisations attribute correctly. Same stripping applies to any declaration whose declarator surfacesFoo<...>as the owning scope. - C++: real inheritance (
class Derived : public Base) now recognised.extract_classconsults thebase_class_clausechild and pulls the firsttype_identifier(ortemplate_type's underlying name) asparent_name. Falls back to the first-field heuristic only when no base clause is present, so legacy C struct-embedding cases still work. Also fixes the class-name extraction for templated classes sotemplate<typename T> class Foo {...}stores"Foo"instead of"Foo<T>". - C++: reference-return methods no longer vanish.
const int& Foo::get()and similarreference_declarator-wrapped definitions used to be silently dropped because tree-sitter-cpp'sreference_declaratorhas nodeclaratorfield — the declarator walker returnedNone. Bothfind_func_name_node(c_lang) anddescend_to_qualified_identifier(cpp) now fall back to the first named child when the field is absent. Same fix path covers reference-return + qualified (const T& Foo::bar()) so class attribution still works. 6 additional regression tests (reference/pointer return types, multi-method attribution, constructor,extern "C", const member) added incha-parser/tests/cpp_enhancements.rs(14 total).
[1.11.1] - 2026-04-27
Changed
- Internal: split git-backed post-analysis passes (
unstable_dependency,bus_factor,low_test_ratio) out ofcha-cli/src/analyze.rsinto a newcha-cli/src/git_metricsmodule. No behaviour change;analyze.rsdrops below the 850-linelarge_filethreshold thatcargo xtask analyzegates on.collect_top_levelin the C parser also picks up// cha:ignore high_complexityalongside the existing cognitive-complexity ignore after thedeclarationarm added one branch.
Note: 1.11.0 was tagged in the repo but the CI self-analyze gate failed on the above source-dir warnings so crates.io was never updated. 1.11.1 is the first shipped release of the 1.11 line.
[1.11.0] - 2026-04-27
Fixed
- Cache invalidation now tracks the cha binary, not
CARGO_PKG_VERSION.env_hashfolds instd::env::current_exe()'s mtime, so any new binary — developer rebuild after editing parser code, or end-user upgrade to a new release — invalidates stale cachedSourceModelentries. The previous version-based key allowed parser behaviour changes shipped without acargo xtask bumpto silently serve wrong cached data (which is what hid the header-declaration parser fix from users with existing.cha/cache). Falls back toCARGO_PKG_VERSIONwhencurrent_exe()fails (unusual — sandboxed runners). - C/C++ parser now extracts function declarations from header files (
void foo(int);— no body). Previously the parser only recognisedfunction_definitionnodes at the top level, silently dropping every prototype in a.hfile. This brokecha deps --type classes --detailon C projects (every widget method displayed as private),leaky_public_signature(blind to the real public API), and thec_oop_enrich::tighten_exportspass (demoted public.cimplementations whose.hdeclaration didn't parse). Variadic + attribute-macro signatures likefoo(..., ...) LV_FORMAT_ATTRIBUTE(4, 5)remain an edge case because tree-sitter-c errors on the macro. Existing.cha/cache/entries are stale after this fix and need to be deleted manually — the cache key hashesCARGO_PKG_VERSION, not parser behaviour.
Changed
- C OOP attribution is now longest-prefix + inheritance-aware: given
struct derived_t { base_t obj; ... }(first-field embedded base),derived_do(base_t *obj)attributes toderived_trather than the base, because the function name's longest matching prefix points at the specific subclass andderived_t's ancestor chain includesbase_t. Eliminates the previous over-attribution where short prefixes caused base classes to absorb methods that morally belong to subclasses. Large C codebases see base classes drop hundreds of borrowed methods; subclasses now correctly show their own methods incha deps --type classes --detailUML. cha deps --type classes --detailnow uses the project-wide C OOP attribution fromc_oop_enrichto fill methods on C/C++ UML output. Previously relied on a same-directory heuristic that missed cross-module methods; now picks up methods on shared metaclasses regardless of which file they live in. Also runs enrich onparse_all_modelsso C models read bydepssee the correctedmethod_count/has_behavior/is_exported.- C OOP cross-file method attribution: new
cha-cli/src/c_oop_enrichmodule runs insideProjectIndex::parseto rewriteClassInfo.method_count/has_behaviorand tightenFunctionInfo.is_exportedfor C / C++ projects. Uses tokenisation (snake_case, PascalCase, camelCase, acronyms) + typedef alias following to attribute free functions to structs via the universalfoo_t+foo_xxx(foo_t *self)convention. Forward declarations and full definitions of the same struct share attribution. Third-party types declared only in.cfiles (no.hdeclaration) get demoted from exported to internal. Only affects post-analysis index-backed detectors (anemic_domain_model,leaky_public_signature, etc.); per-file Plugin detectors (lazy_class,data_class) still see the unenriched model and remain disabled in the C profile. - Replaces the previous same-file
associate_methodsincha-parser::c_lang(deleted) and the same-directoryc_oop_filterpost-hoc filter incha-cli(deleted) with a single project-wide enrichment pass.
Added
cha analyze --focus <category>— comma-separated filter keeping only findings whoseSmellCategorymatches one of the listed values (bloaters,oo_abusers,change_preventers,dispensables,couplers,security). Unknown categories warn on stderr instead of crashing. Lets users narrow a noisy analyze run to a single architectural concern.Finding.risk_score: Option<f64>— composite priority (severity × overshoot × hotspot factor) populated byprioritize_findingsafter analysis. Surfaces why a finding ranks where it does in reporter output and JSON/SARIF. Schema regenerated.leaky_public_signature— flags an exported function whose parameters or return type mention a third-party crate's type. Workspace-internal crates (derived from project file paths) and Rust's built-in modules (std,core,alloc,proc_macro, …) are filtered out so intra-workspace and prelude types don't fire. Hint severity.cross_layer_import— post-analysis pass that automatically infers project layers from the import graph (Martin's instability) and flags imports crossing boundaries upward. No configuration required; defers to the existinglayer_violationplugin when the user has written an explicit[plugins.layer_violation]config. Warning severity (architectural violation).
[1.10.0] - 2026-04-25
Added
god_config— flags aConfig/Settings/Options/Context/Env/AppState/Store-shaped type (exact name or*Config/*Settings/*Optionssuffix) passed as a parameter to ≥ 10 distinct functions spanning ≥ 3 files. Signals ambient configuration leaking everywhere instead of each caller taking only the fields it actually needs. Hint severity.circular_abstraction— flags two files whose functions call each other's functions in both directions (≥ 2 calls each way). Catches behaviour-level mutual dependency that import-graph cycle detection misses when the callees are re-exported or wrapped. Complementstyped_intimacy(type flow) with call flow. Hint severity.parameter_position_inconsistency— flags functions where a domain type appears at a different parameter position than the project-wide majority. Requires ≥ 3 usages of the same type across functions and disagreement on position; primitives, unresolved-origin types, mutable-ref out-params (&mut Vec<_>), andselfreceivers are skipped. Hint severity.
Changed
- Internal:
cha-cli/src/project_index.rs— sharedProjectIndexowns parsed models plus derived maps (function_home, class_home, project_type_names, function_by_name).anemic_domain_model,typed_intimacy,module_envy, andparameter_position_inconsistencybuild the index once per analyze call instead of each rebuilding their own copies. No behaviour change; behaviourally identical on self-analyze. Boundary_leak still parses fresh because of a stale-typedef cache bug not yet rooted out.
[1.9.0] - 2026-04-25
Added
module_envy— flags a function that makes ≥ 3 calls into another file in the project while making ≤ half as many calls within its own file. The function is a "resident" of the wrong module — its body does work that belongs in the envied module. Suppresses test →common.rspairs and calls to conventional helper filenames (utils,helpers,shared,prelude, …) where cross-file dependency is idiomatic, not misplaced. Hint severity.typed_intimacy— flags file pairs whose function signatures exchange each other's declared types in both directions. Stronger signal than import-levelinappropriate_intimacy: the pair literally accepts/returns types defined in each other, indicating they're functionally fused at the type boundary. Emits one finding per side of the pair, listing the shared type names. Hint severity.async_callback_leak— flags a function signature that exposes a raw concurrency primitive (JoinHandle,Future,Task,Sender,Receiver,Promise,Awaitable,Coroutine,CancelFunc, …) in its return type or parameters. Skips launcher-shaped names (spawn_*,launch_*,start_*) where exposing the handle is the function's whole purpose. Hint severity.anemic_domain_model— flags a class that is pure data (≥ 2 fields, no behavior) paired with one or more external service-shaped functions (filename ends inservice/manager/handler/helper/util, or function name starts with a service verb prefix likeprocess_/validate_/calculate_) that take the class as a first parameter. Promotes adata_classhint into an architectural finding when there's evidence the paired service owns behavior that should live on the class itself. Hint severity.test_only_type_in_production— warns when production code references a class/struct declared only in test files (mocks, stubs, fixtures). Surfaces test scaffolding bleeding into shipping code. Warning severity.return_type_leakpost-analysis finding — dual ofabstraction_boundary_leak. Detects when a dispatcher fans out to ≥ 3 sibling handlers whose return types are all the same non-local type, surfacing missing Anti-Corruption Layer on the way out. lvgl scan identifies thorvg'sTVG_APIleaking through dispatcher boundaries.FunctionInfo.return_type: Option<TypeRef>— parsers extract the declared return type and resolve its origin through the same imports/type-registry pipeline as parameters. WIT schema grows an optionalreturn-typefield.- Container-expression primitive fallback: PEP 585
dict[K, V]/list[T]/tuple[...]resolve to Primitive instead of Unknown, eliminating false positives on Python handlers that return built-in container types.
Changed
- WIT
function-inforecord gainsreturn-type: option<type-ref>— breaking for WASM plugins, rebuild against the new SDK. cha-cli/src/analyze.rs— extracted C OOP false-positive filter toc_oop_filter.rsand splitrun_post_analysisinto git-based and signature-based helpers to keep the orchestrator lean as more post-analysis passes land.
[1.8.0] - 2026-04-25
Added
abstraction_boundary_leakpost-analysis finding — detects dispatcher functions that fan out to ≥ 3 sibling callbacks which all share the same non-local type in corresponding parameter positions. Flagged as a missing Anti-Corruption Layer. lvgl scan shows 11/13 true-positive rate identifying GLAD/SDL/STB/Win32 leaks.FunctionInfo.parameter_typesnow carriesTypeRef { name, raw, origin }whereoriginisLocal | External(module) | Primitive | Unknown. Each parser resolves origins from file imports: Rustuse_declaration, TSimport_statement, Pythonimport/from, Goimport_specwithgo.modmodule root lookup, C/C++ primitive seeding.- Parser normalisation helpers in
cha-parser/src/type_ref.rsunwrap&'a mut Vec<Option<T>>,[]T,List[T],pkg.Typeetc. down to the innermost identifier for import lookup. - Universal-primitive fallback in resolve (String, PathBuf, HashMap, int, boolean, etc.) so common prelude types without explicit imports don't trip the detector.
unwrap_abusenow emits one finding per.unwrap()/.expect(call site (was: single finding at function name). IDE underlines each call directly.switch_statementnow points at theswitch/matchkeyword inside the function body (was: function name).message_chainnow points at thea.b.c.dchain expression itself (was: function name). Heuristic text scan, falls back to function name when the chain can't be textually located.
Changed
FunctionInfo.parameter_typestype changed fromVec<String>toVec<TypeRef>— breaking change for WASM plugins and cached SourceModels. WIT schema addstype-refrecord andtype-originvariant. Rebuilding against the new SDK picks up generated types automatically.- Parsers no longer sort
parameter_types— declaration order is preserved, fixing latent.first()-based C OOP heuristics that silently depended on alphabetical ordering.data_clumpsplugin now sorts its own key locally.
[1.7.1] - 2026-04-24
Fixed
cargo xtask release—wait_for_workflownow filters runs by the commit SHA (for ci.yml) and the tag branch (for release.yml), instead of taking the latest run unconditionally. Previously a stale success on an unrelated commit would cause the release flow to skip waiting and publish to crates.io while the new CI was still queued; a stale failure would abort a release that would otherwise pass.
[1.7.0] - 2026-04-23
Added
cha analyze --top Nflag — show only the N most severe findings (terminal format), complements--all- Smell-level disable:
disabled_smells = ["smell_name"]in.cha.toml(global) or under[languages.<lang>](language-scoped). Finer-grained than disabling a whole plugin when it produces multiple smells Plugin::smells()— plugins declare whichsmell_namevalues they can produce. Exposed as a WIT export for WASM pluginscha plugin listnow shows each plugin's declared smellscha preset show <lang>now shows effective disabled smells- SDK helper
cha_plugin_sdk::is_smell_disabled!(&input.options, "smell_name")— WASM plugins can skip disabled work proactively
Changed
- C/C++ builtin profile:
builder_pattern,null_object_pattern,strategy_pattern,data_clumpsare now properly disabled via smell-level config (previously tried — and failed — to disable them by plugin name) - WIT
analyzerworld gainssmells: func() -> list<string>export — breaking change for WASM plugins (recompile to pick up default impl)
Fixed
- lvgl-scale improvement: analyze now emits ~1200 fewer false positives because smell-level disables actually take effect
[1.6.0] - 2026-04-23
Added
Locationnow hasstart_col/end_colfields — all findings precise to column levelFunctionInfo/ClassInfohavename_col/name_end_col— parser records identifier positionImportInfohascol— import statement column position- Terminal output shows
file:line:colwhen column info available - SARIF output fills
startColumn/endColumn(1-based per spec) - LSP diagnostics use precise column range
Changed
- All 37 builtin plugins now point findings at the function/class name, not the entire body
- Line-scanning plugins (unsafe_api, hardcoded_secret, todo_tracker, error_handling) report exact column
- WIT records gain column fields —
location.start-col/end-col,function-info.name-col/name-end-col,class-info.name-col/name-end-col,import-info.col— breaking change for WASM plugins
[1.5.0] - 2026-04-22
Added
- VS Code
cha.disabledPluginssetting — suppress specific findings viainitializationOptions - Hover report card shows actual plugin findings with severity icons
- Coupling/hub_like findings mark import line range precisely
Changed
- LSP architecture: all handlers read from ProjectCache — no per-handler plugin execution
- LSP uses pull-only diagnostics (
textDocument/diagnostic), removed push duplicates - CodeLens shows findings count + severity instead of raw parse metrics
- Inlay Hints show findings summary (⚠N or ✓)
- File-level findings (large_file, shotgun_surgery, etc.) mark only line 1
Fixed
- Duplicate diagnostics (push + pull) in VS Code
disabledPluginsnow filters by finding name, not plugin name- LSP shares
.cha/cache/with CLI via ProjectCache
[1.4.2] - 2026-04-22
Added
- VS Code: auto-detect outdated cha binary — prompt update when version mismatches extension
- VS Code: debug logs in ensureBinary for diagnostics
- VS Code e2e: real VS Code test on 3 platforms (ubuntu/macos/windows) with sinon stub for user Download click
Fixed
- SDK macros: include build.rs in package
- VS Code: Windows download (.zip + PowerShell + .exe)
- VS Code: exclude test files from .vsix via .vscodeignore
- CI: vscode e2e set continue-on-error for network flakiness
[1.4.1] - 2026-04-21
Added
- VS Code extension CI:
vsce packagevalidation + download e2e test on GitHub Actions - Download e2e test imports actual extension code (shared
download.tsmodule)
Fixed
- Windows binary download: use
.zip+ PowerShell extraction +.exebinary name
[1.4.0] - 2026-04-21
Added
- LSP Semantic Tokens: highlight functions/classes with warning modifier based on findings
- LSP Workspace Diagnostics: full project analysis without opening files
- LSP textDocument/diagnostic: pull-based diagnostics per file
- LSP Progress: progress notification during workspace diagnostics scan
[1.3.0] - 2026-04-21
Added
- LSP Document Symbols: outline view with ⚠ markers based on actual findings severity
- LSP: Document Symbols ⚠ markers now respect
.cha.tomlthresholds (no hardcoded values)
Changed
- Upgraded wasmtime 43 → 44
- Include tests in cha-core crate package (eliminates publish warnings)
[1.2.0] - 2026-04-21
Added
- LSP CodeLens: show complexity, cognitive, lines, params above every function/class
- LSP Hover: detailed quality report card on hover (markdown table)
- LSP Inlay Hints: inline cx/cog/lines annotations at end of function definitions
[1.1.0] - 2026-04-21
Added
- Cache v2: bincode serialization + per-file parse cache + mtime fast-path
- L1 in-memory parse cache — zero disk I/O for repeated access within same process
- Cached imports in meta for instant
unstable_dependencyanalysis ProjectCachewith L1/L2 architecture shared across analyze/layers/deps/calibrate
Changed
- Performance:
cha analyze26x faster on warm cache (87s → 3.3s on 3201 files) - Performance:
cha layers16x faster (13s → 0.8s) - Performance:
cha deps14x faster (13s → 0.9s) - Performance:
cha calibrate22x faster (13s → 0.6s)
Fixed
- O(n²) algorithm in
unstable_dependency/compute_afferentreplaced with HashMap O(1) lookup - Findings cache wiped by duplicate
ProjectCache::openin post-analysis - Cache invalidation now includes cha binary version (upgrade = auto-invalidate)
- Skip
filter_c_oop_false_positiveswhen no lazy_class/data_class findings exist
[1.0.10] - 2026-04-21
Added
- Global
--config <path>flag for all subcommands — load config from custom file ImportInfo.is_module_declfield to distinguish module declarations from imports
Fixed
- Rust
moddeclarations no longer inflatehigh_couplingcount
[1.0.9] - 2026-04-20
Added
cha layers --format html— interactive architecture diagram with CSS Grid- Layer violations show file-level evidence (which file includes which)
- Layer violations sorted by instability gap (most severe first)
- Rust
moddeclarations treated as file imports for layer analysis - Manual layer/module config in
.cha.toml([layers.modules]+[[layers.tiers]])
[1.0.8] - 2026-04-20
Added
cha calibratecommand: auto-suggest thresholds from project statistics (P90/P95)cha calibrate --applysaves to.cha/calibration.toml, auto-applied bycha analyze- Finding priority sorting: most severe issues shown first (severity × overshoot × compound)
- Short module names in all output formats (terminal/DSM/dot/mermaid)
Changed
- DSM output limited to top 25 modules by file count
Fixed
- Skip parent→child layer violations (reduces lvgl false positives 87→37)
[1.0.7] - 2026-04-20
Added
- Module inference rewrite: directory elbow + LCOM4 adaptive split + ICR + TCC quality metrics
cha layers --depth Nto override auto-detected directory depthcha layers --format dsm|terminaloutput formats- Composite risk scoring for
long_method:risk = lines_ratio × complexity_factor
Changed
- Module inference algorithm: replaced Union-Find with directory elbow + LCOM4 + ICR
long_methodseverity now based on composite risk (Hint/Warning/Error at risk 1/2/4)
Fixed
cha:ignoredirective now covers up to 2 lines before a function- Fixed corrupted dot output and switched to LR layout for better layer readability
[1.0.6] - 2026-04-20
Added
- Language-adaptive thresholds: C/C++ profile with higher defaults (long_method=100, complexity=15, large_file=2000)
- Smart terminal aggregation: findings >5 grouped into summary + top 3 worst,
--allflag for full listing cha layerscommand: infer architectural layers from import dependenciescha layers --format dot|mermaid|json|plantumlwith layered architecture diagram
[1.0.5] - 2026-04-17
Fixed
- VS Code extension: download URL corrected (
cha-cli-prefix), extract path for cargo-dist tarball - VS Code extension: download with progress bar and cancellation support
- VS Code extension: removed system PATH fallback for reliable self-testing
cargo publishno longer needs--allow-dirty(WIT copies tracked in git,includein Cargo.toml)
[1.0.4] - 2026-04-17
Added
cha:setinline directive: override thresholds per-function/class via comments (// cha:set rule_name=value)Finding.actual_valueandFinding.thresholdfields for post-filter re-evaluationcha lspsubcommand: start LSP server from unified binary (+3MB)deps --direction in|out|both: filter edges by direction (who depends on target vs target depends on)deps --format plantuml: PlantUML output for component and class diagrams- C OOP false positive filter: removes lazy_class/data_class for structs with cross-file methods
.pre-commit-hooks.yaml: pre-commit framework integrationaction.yml: GitHub Action for CI analysis with SARIF upload- VS Code extension (
vscode-cha/): cha LSP integration, auto-download binary, esbuild bundle
Fixed
.hfiles with C++ constructs now parsed as C++ (content sniffing)class MACRO Name {}no longer misidentified as function definition- WIT
Findingrecord now includesactual_value/thresholdfields build.rsauto-copieswit/plugin.witfor crates.io packaging- VS Code extension: esbuild bundle, LICENSE,
.vscodeignore, publisher ID, homepage
[0.7.0] - 2026-04-17
Added
- Dynamic shell completion for
--pluginviaCompleteEnv(unstable-dynamic):eval "$(COMPLETE=zsh cha)" PluginRegistry::plugin_info()for runtime plugin discovery with descriptionsPlugintrait unified:version(),description(),authors()with defaults from Cargo.toml- All 33 builtin plugins now have description text for shell completion
completionssubcommand now outputs dynamic completion scripts; shows usage when called without args--strictnessflag:relaxed(2x),default(1x),strict(0.5x), or custom float — scales all numeric thresholds- Per-language plugin config:
[languages.c.plugins.naming]overrides in.cha.toml - Builtin C language profile: disables naming, lazy_class, data_class, builder/null_object/strategy pattern by default
cha preset list/showsubcommand — display language profiles and plugin rulesSourceModel.type_aliases— unified typedef/type alias tracking across all languages- C OOP heuristic: associate functions with structs via inheritance chain + same-module matching
--exact --detailnow shows only directly matched classes, not parents/children- C parser
extract_paramsnow includes pointer info (Type *) from AST - UML class diagrams:
staticfunctions shown as private (-), non-static as public (+)
Changed
Configstruct now hasstrictnessandlanguagesfields (fully backward compatible)get_usize()applies strictness scaling factor automaticallycmd_analyzerefactored intoAnalyzeOpts+run_post_analysis()+apply_filters()parse_all_modelsreturns(PathBuf, SourceModel)pairs for correct file-model association
Fixed
- C/C++ parser:
staticfunctions now correctly markedis_exported = false; header files always exported - Reduces
large_api_surfacefalse positives by ~51% and enables accuratedead_codedetection for C shotgun_surgery,divergent_change,bus_factornow use single batchgit logcall instead of per-file — fixes freeze on large repos (lvgl: >2min → 23s)- C OOP method association resolves typedef aliases for cross-file matching
class_dirprefers struct definitions with fields over forward declarations
[0.6.2] - 2026-04-15
Added
- All parser fields implemented for C/C++, Go, Python (zero TODO(parser) remaining)
- C-style struct inheritance detection via first-field type + typedef alias resolution
--filternow shows connected subgraph (children + parent chain, no siblings)--exactflag for direct-match-only filtering--filtersupports regex patterns--detailflag for UML class diagrams with fields, types, and methodsClassInfo.field_typesfield across all parsers and WIT interface
Fixed
- C parser: recurse into
#ifdef/#ifpreprocessor blocks for struct/include detection - C parser:
typedef struct { ... } Namenow correctly parsed - Filter traversal: parent chain walk without sibling expansion; fixed infinite loop
[0.6.1] - 2026-04-14
Added
SourceModel.comments— parsers now extract comments via tree-sitter for language-aware analysistodo_trackernow uses parsed comment nodes instead of raw text scanning
Fixed
cha trend— suppressed git worktree stdout leak; fixed progress bar overlap- Progress bar spinner now uses braille animation with steady tick
- Extracted
new_progress_barhelper; added progress bars tocha deps - Unimplemented parser fields marked with
TODO(parser)comments for self-tracking
[0.6.0] - 2026-04-14
Added
god_classplugin — God Class detection (ATFD>5, WMC>=47, TCC<0.33) [Lanza & Marinescu 2006]brain_methodplugin — Brain Method detection (LOC>65, CYCLO>=4, NOAV>7) [Lanza & Marinescu 2006]hub_like_dependencyplugin — detect modules with excessive import fan-out [Arcelli Fontana et al. 2019]error_handlingplugin — detect empty catch blocks and unwrap/expect abuse [Padua & Shang 2018]unstable_dependency— post-analysis pass using Martin's instability metric I=Ce/(Ca+Ce)cognitive_complexityplugin — nesting-aware complexity metric, threshold 15 [SonarSource 2017]todo_trackerplugin — detect leftover TODO/FIXME/HACK/XXX commentsunsafe_apiplugin — detect dangerous function calls per language [CWE-676]low_test_ratio— warn when test code < 50% of production codetangled_change— detect commits touching unrelated modules [Tornhill 2015]bus_factor— knowledge distribution risk detection [Nagappan et al. 2008]cha hotspotsubcommand — git change frequency × complexity [Tornhill 2015]
Fixed
- Duplicate plugin registration bug in
register_advanced_plugins
[0.5.2] - 2026-04-13
Added
cha trendsubcommand — analyze recent git commits via worktree, show issue count trend (terminal ASCII + JSON)// cha:ignorecomment directive — suppress findings per function/line, supports//,#,--,/* */stylescha deps --type classes— class hierarchy graph (extends/implements)cha deps --type calls— function call graph with recursion detection (blue dashed)cha deps --filter <name>— filter graph to specific class/function
Fixed
- Cache invalidation now scans all
.cha.tomlfiles in subdirectories, not just root
[0.5.1] - 2026-04-12
Added
cha deps --type classes— class/struct/trait hierarchy graph (extends/implements)cha deps --type calls— function call graph with recursion detection (blue dashed lines)cha deps --filter <name>— filter graph to specific class/functionFunctionInfo.called_functionsfield in parser output and WIT interface
[0.5.0] - 2026-04-12
Added
cha depssubcommand — import dependency graph with--format dot|json|mermaid,--depth file|dir, cycle detection with red highlighting- Go language support (.go) — functions, structs, interfaces, imports, complexity, chain depth
- C language support (.c/.h) — functions, structs, includes, complexity
- C++ language support (.cpp/.cc/.cxx/.hpp/.hxx) — functions, classes, includes, complexity
- Health scores in JSON output (
health_scoresfield) and SARIF output (properties.health_scores) [debt_weights]config section in.cha.toml— customize remediation time per severity (hint/warning/error)- Plugin-level parallel analysis via rayon
par_iter
Fixed
- HTML report: show only ±5 context lines around findings instead of full file source, collapse file sections by default
0.4.0 - 2026-04-11
Added
- Tech debt summary in terminal output: total estimated remediation time + grade distribution
--format html— self-contained HTML report with dark theme, source code highlighting, health scores, and collapsible file sections--output <path>flag to write report to filehardcoded_secretplugin — detects API keys, tokens, passwords, private keys, JWTs in source codeSmellCategory::Securityvariant for security-related findings
0.3.0 - 2026-04-10
Added
- Incremental analysis cache (
.cha/cache/) — skips unchanged files, ~70x speedup on warm runs --no-cacheflag to force full re-analysis- Cache auto-invalidates when
.cha.tomlor plugins change cha baseline— generate a baseline file of current findings, suppress known issues--baseline <path>flag oncha analyzeto only report new findings- Code health scores (A–F) per file in terminal output, based on issue density and severity
0.2.0 - 2026-04-10
Added
- Python language support (.py) — functions, classes, imports, complexity, chain depth, delegating detection
Fixed
xtask bumpnow dynamically scans all Cargo.toml files instead of hardcoded paths, and refreshes all Cargo.lock files- Duplicate
PythonParserimport in cha-parser cha-lsp/Cargo.tomlversion not updated byxtask bump
0.1.1 - 2026-04-10
Added
cha completions <shell>— generate shell completion scripts (bash/zsh/fish/powershell); auto-installed via Homebrew
Fixed
cha plugin newhint now showscha plugin buildinstead ofcargo build, and uses correct underscore filename- WASM plugin e2e test: plugin dir detection when
cha plugin newuses cwd directly - Unused
Pathimport incha-plugin-sdktest-utils
Changed
cha-lsp: markedpublish = false, not distributed via crates.ioxtask: refactoredcmd_publish/cmd_bumpto reduce complexity
0.1.0 - 2026-04-10
Added
Core Analysis
- 25 built-in code smell plugins covering Bloaters, Couplers, OO Abusers, Change Preventers, and Dispensables
- 9 new plugins: TemporaryField, SpeculativeGenerality, RefusedBequest, ShotgunSurgery, DivergentChange, LazyClass, DataClass, MiddleMan, FeatureEnvy
- DesignPatternAdvisor: suggests Strategy, State, Builder, Null Object, Template Method, Observer patterns
- TypeScript and Rust AST parsing via Tree-sitter
- Structural duplication detection via AST hash
WASM Plugin System
- WIT interface with full model fields (
FunctionInfo,ClassInfo) and typedoption-valuevariant cha-plugin-sdkcrate: zero-config plugin development — no WIT file needed,plugin!macro embeds WIT at compile timecha plugin new/build/install/list/removeCLI subcommands- Auto-conversion of WASM binary to WASM Component in
cha plugin build test-utilsfeature:WasmPluginTestbuilder for plugin unit testing- Plugin metadata (version, description, authors) auto-filled from plugin's
Cargo.toml - Config options passed from
.cha.tomlto plugins as typedOptionValue
CLI
cha analyze— recursive analysis with.gitignoreawareness,--diff,--stdin-diff,--pluginfiltercha parse— inspect AST structurecha init— generate default configcha fix— auto-fix naming violationscha schema— print JSON Schema for output format- Output formats: terminal, JSON, SARIF, LLM context
--fail-onexit code control
LSP
- Real-time diagnostics on open/change/save
- Code action suggestions
Tooling
cargo xtask ci/build/test/lint/analyze/lsp-test/plugin-test/plugin-e2ecargo xtask bump <major|minor|patch>— version bump across all cratescargo xtask publish [--dry-run]— publish to crates.io in topological order- cargo-dist: multi-platform binaries (macOS/Linux/Windows), shell/powershell/homebrew/msi installers
- oranda: project website with release artifacts