cherry-studio's AI core and MCP layer carry the highest activity risk — 5 functions to address first

cherry-studio's top activity-risk functions — initClient in MCPService.ts and getReasoningEffort in reasoning.ts — combine extreme structural complexity with live commit churn, flagging them as immediate regression risks.

Stephen Collins ·
oss typescript refactoring code-health
Activity Risk19.66Low
Hottest FunctioninitClient

Antipatterns Detected

god_function6long_function6complex_branching5deeply_nested5exit_heavy5hub_function1middle_man1

Key Points

What is a god function and why does it matter in cherry-studio?

A god function is one that does too much — it calls a large number of other functions, handles many responsibilities, and becomes the single point through which broad system behavior flows. Fan-out is the count of distinct functions a given function directly calls; a fan-out of 15 or more is a strong signal, and cherry-studio's initClient reaches 46 distinct callees, making it a textbook example. The concrete problem is blast-radius risk: any change to initClient can disturb any of those 46 downstream functions in unexpected ways, and testing the function in isolation requires mocking nearly half the service layer. The analysis found 6 god-function instances among cherry-studio's top hotspots, concentrated in the MCP service and AI core layers where coupling is most costly.

How do I reduce cyclomatic complexity in TypeScript?

The most effective technique is extract-method refactoring: identify each major conditional branch or case block and pull it into a named function with a clear single responsibility, reducing the root function to a readable routing shell. A cyclomatic complexity above 15 warrants splitting; above 30 warrants immediate attention; getReasoningEffort's CC of 132 means it needs to be broken into at least a dozen independently testable units. A concrete first step today: open getReasoningEffort, find the outermost switch or if-else chain, and move each case body into its own function — even that single extraction will drop the measured CC significantly and make the remaining structure easier to reason about. Pairing this with a table-driven or strategy-pattern approach prevents the complexity from re-accumulating as new providers or effort tiers are added.

Is cherry-studio actively maintained?

Yes — the data shows clear active development at the project's most structurally complex points. initClient in MCPService.ts has been touched twice in the last 30 days and was last changed just 1 day ago; getReasoningEffort in reasoning.ts was last changed 6 days ago. Both are fire-quadrant functions, meaning high structural complexity and recent commit activity are occurring simultaneously. Active maintenance and structural complexity are not mutually exclusive — in fact, the fire-quadrant classification is precisely the signal that the team is shipping features through some of the hardest-to-change code in the repo.

How do I reproduce this analysis?

The analysis was run against commit `a574aa5` of CherryHQ/cherry-studio using the hotspots CLI, available at https://github.com/hotspots-dev/hotspots. After running `git checkout a574aa5` in your local clone, execute `hotspots analyze . --mode snapshot --explain-patterns --force` to reproduce the exact results. The same command works on any local git repository without additional configuration.

What does activity-weighted risk mean?

Activity-weighted risk combines structural complexity — cyclomatic complexity, nesting depth, and fan-out — with recent commit frequency, so that functions which are both hard to understand and actively changing score the highest. A function with cyclomatic complexity 80 that hasn't been touched in two years scores much lower than one with CC 20 that is touched every week, because the complex-but-dormant function poses lower near-term regression risk — no one is currently navigating its execution paths and introducing bugs. This prioritization helps teams focus refactoring effort where it reduces the probability of bugs being introduced right now: initClient's recent commit activity of 18.63 combined with its CC of 25 and fan-out of 46 is exactly this scenario — structural complexity being actively edited, which is where regressions happen in practice, not just in the abstract.

cherry-studio’s highest activity-risk function, initClient in MCPService.ts, carries an activity risk score of 19.66 with a recent commit activity of 18.63 — it was modified just 1 day ago and has been touched twice in the last 30 days, making its cyclomatic complexity of 25, max nesting depth of 9, and fan-out of 46 a live regression risk rather than a cleanup backlog item. The project spans 5,073 analyzed functions, 479 of which are rated critical, and every one of the top-ranked hotspots lands in the ‘fire’ quadrant — meaning high structural complexity and active recent change are occurring simultaneously across the codebase’s most sensitive layers. Cherry-studio appears to be an AI desktop client integrating multiple model providers and tooling protocols, and the data shows that both its MCP service layer and its AI reasoning utilities are under active development at precisely the points of highest structural complexity.

The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).

Top 5 Hotspots

FunctionFileRiskCCNDFO
initClientsrc/main/services/MCPService.ts19.725946
getReasoningEffortsrc/renderer/src/aiCore/utils/reasoning.ts19.1132637
runsrc/main/services/CodeToolsService.ts18.766552
load_run_resultsresources/skills/skill-creator/scripts/aggregate_benchmark.py18.651628
backupToS3src/renderer/src/services/BackupService.ts18.032724

Large Repo Analysis

cherry-studio is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.

Codemod / Tooling Files in Results

The function _0x18b2 in src/main/integration/nutstore/sso/lib/index.mjs is a vendored or bundled third-party SSO library — the obfuscated name and .mjs path under a lib/ subdirectory are strong indicators of a bundled dependency rather than project-authored code. Its metrics (CC 5, ND 2, FO 4) are low-risk and its activity score reflects commits to the integration wrapper, not the library itself. Exclude it from future analyses with: { "exclude": ["src/main/integration/nutstore/sso/lib/"] } in your .hotspotsrc.json.

Hotspot Analysis

initClient — src/main/services/MCPService.ts

Based on its name and location in MCPService.ts, initClient almost certainly bootstraps a Model Context Protocol client — establishing connections, configuring transports, and wiring up the service layer that downstream AI tool calls depend on. Its cyclomatic complexity of 25 means there are at least 25 independent execution paths through initialization logic, its max nesting depth of 9 is a strong refactoring signal on its own, and its fan-out of 46 means it directly calls 46 distinct functions — making it a true god function whose blast radius touches nearly every service the MCP layer exposes. This is a fire-quadrant function: it was last changed just 1 day ago and has 2 commits touching it in the last 30 days, so that CC-25/ND-9/FO-46 structure is being actively navigated by contributors right now, which is where live regressions are born.

Recommendation: Before any refactoring, add characterization tests that exercise each of the major initialization branches — the exit-heavy pattern flagged in the data means multiple early-return paths exist and are easy to miss. Then extract sub-functions by responsibility (transport setup, capability negotiation, error handling) to bring fan-out and nesting depth down to reviewable levels; targeting ND ≤ 4 and FO ≤ 15 per extracted unit would cut the blast radius substantially.

getReasoningEffort — src/renderer/src/aiCore/utils/reasoning.ts

Sitting in the renderer-side AI core utilities, getReasoningEffort almost certainly maps model configurations or user settings to a reasoning effort level — likely a classification or dispatch function that branches across many supported providers or effort tiers. Its cyclomatic complexity of 132 is extreme by any standard: CC 100+ represents a function with over a hundred independent execution paths, each one a required test case and a potential bug surface. A fan-out of 37 reinforces the god-function characterization — it reaches into 37 other functions — and while its max nesting depth of 6 is lower than initClient’s, the sheer scale of branching (CC 132) means the cognitive load of understanding any single change through it is enormous. It is a fire-quadrant function touched once in the last 30 days and last changed 6 days ago, meaning active iteration is happening through a function of extraordinary complexity.

Recommendation: A CC of 132 warrants immediate decomposition: the most effective first step is to identify the top-level dispatch axis — likely provider type or effort tier — and extract each branch into its own named function, reducing the root function to a pure routing shell. Pair this with a table-driven or strategy-pattern approach so that adding a new provider or effort level does not require adding yet another conditional branch to an already unmaintainable function.

run — src/main/services/CodeToolsService.ts

run in CodeToolsService.ts is almost certainly the main execution dispatcher for cherry-studio’s code tooling layer — responsible for invoking terminal commands, scripts, or sandboxed code execution on behalf of the AI. Its cyclomatic complexity of 66 with a max nesting depth of only 5 is a signature of broad sequential branching rather than deep nesting: many tool types, runtime environments, or execution modes each handled by their own branch. The fan-out of 52 is the highest in the top 5, meaning this single function reaches into 52 distinct downstream callees — a fan-out that makes it a true god function whose internal changes ripple across half the tools service layer. An activity risk of 18.7 confirms it is under active development at this level of complexity.

Recommendation: The fan-out of 52 is the primary risk signal here: changes to run can disturb 52 downstream callees, and testing it in isolation requires stubbing most of the tools layer. Extract each tool type or execution mode into a dedicated handler and route through a dispatch table; this reduces fan-out at the root, eliminates the per-tool branching, and makes each handler independently testable.

load_run_results — resources/skills/skill-creator/scripts/aggregate_benchmark.py

load_run_results lives in a Python benchmark aggregation script under resources/skills/skill-creator/ — a path that suggests it is part of the tooling used to evaluate or calibrate skill-creator capabilities, likely loading and merging results from multiple benchmark runs. A CC of 51 with ND 6 and FO 28 indicates a function that handles many result formats or edge cases (CC 51) through moderately deep nesting (ND 6) while calling into 28 distinct downstream helpers. Data-loading functions in benchmark scripts commonly accumulate this shape when they need to handle partial results, missing files, version mismatches, and format variations — each case adding a branch.

Recommendation: Separate the concerns of loading raw results from the concerns of validating and normalizing them. A loader that simply reads files and a normalizer that handles format variations and missing data are independently testable and much easier to extend. Targeting CC ≤ 15 per function and ND ≤ 4 would bring this into a maintainable range.

backupToS3 — src/renderer/src/services/BackupService.ts

backupToS3 handles cloud backup of user data to S3 — a function that must coordinate authentication, serialization, chunked upload, error handling, and retry logic, all of which contribute branches. Its CC of 32 with ND 7 is notable: the nesting depth is relatively high for the complexity level, which suggests the branching is driven by nested error-handling or retry logic rather than a flat dispatch across many cases. A fan-out of 24 means it reaches into 24 downstream callees, touching auth, serialization, and upload utilities. An activity risk of 18.0 indicates this is not dormant infrastructure — it is being actively changed.

Recommendation: The ND 7 signal points to nested try/catch or retry structures as the primary refactoring target. Extract the retry/error-handling wrapper into its own reusable function, and separate the serialization and upload phases so each can be tested without the others. This directly reduces nesting depth and isolates the most fragile logic — error handling and retries — from the core upload path.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
god_function6
long_function6
complex_branching5
deeply_nested5
exit_heavy5
hub_function1
middle_man1

These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.

Key Takeaways

  • initClient in MCPService.ts has a fan-out of 46 and was last modified 1 day ago — add characterization tests covering its exit-heavy paths before the next change lands, or risk a regression that propagates across nearly every function it calls.
  • getReasoningEffort carries a cyclomatic complexity of 132, which means over 100 execution paths are effectively untestable as a unit — decompose it along its primary dispatch axis (likely provider or effort tier) using a strategy pattern to make each path independently verifiable.
  • Every top-ranked function in cherry-studio falls in the fire quadrant — there are no debt-quadrant functions in the dataset, meaning the team is actively iterating at the points of highest structural complexity; prioritize test coverage on initClient and getReasoningEffort before adding new branching logic to either.

Reproduce This Analysis

git clone https://github.com/CherryHQ/cherry-studio
cd cherry-studio
git checkout a574aa5e3fc92919cbbeb04f3e4a6a439a30c0aa
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5

To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.

Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →

Run this on your own codebase

Hotspots runs locally in under a minute — no account, no data leaves your machine.

macOS
$ brew install Stephen-Collins-tech/tap/hotspots
Linux / cargo
$ cargo install hotspots-cli
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Related Analyses