kevmoo/analytica.dart · Archived

dart-cognitive-complexity

>- Evaluates and reduces Cognitive Complexity in Dart and Flutter code using deterministic CLI tooling and architectural refactoring patterns (exhaustive pattern matching, guard clauses, method decomposition). Use when reviewing codebase readability, remediating high-complexity warnings, or analyzing structural code health. Don't use for general code formatting, simple syntactic lints, or non-Dart/Flutter repositories.

First seen Aug 4, 2026

Installation

$ npx skills add kevmoo/analytica.dart --skill dart-cognitive-complexity

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from kevmoo/analytica.dart.

npx skills add kevmoo/analytica.dart

Browse all from kevmoo/analytica.dart

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 77
License LICENSE
Default branch main
Open issues 15
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

LicenseApache-2.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 18,497 B
  • docs SUMMARY.md 452 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 27 installs

SKILL.md

1. When to Use This Skill

Use this skill when analyzing Dart and Flutter codebase maintainability, evaluating function readability, or remediating high-complexity findings during code review or static analysis audits.

Unlike Cyclomatic Complexity (which linearly counts control flow branching paths and punishes declarative table switches), Cognitive Complexity measures the mental friction required for a human engineer to read and simulate control flow. Rely on deterministic evaluation to target structures matching these indicators:

  • Deeply Nested Control Flow: Functions exhibiting multiple layers of

enclosing conditionals (if, for, while), where horizontal indentation obscures logic.

  • Convoluted Conditional Trees: Functions employing verbose if-else or

else if chains instead of modern Dart 3 exhaustive pattern matching or table-driven switch expressions.

  • Monolithic Method Bodies: Functions breaching operational threshold

ceilings.

  • God Classes: Logic classes exceeding structural line-count targets

(excluding declarative Flutter build methods).


2. Automated Execution & Scope Resolution

Execute the official package CLI directly in the terminal to retrieve exact complexity scores deterministically without LLM arithmetic or AST interpretation.

SDK Compatibility Note: Executing dart run cognitive_complexity@^0.2.4
requires Dart SDK version 3.12.0 or greater installed in the host
environment. Verify compatibility via dart --version before initiating
scans.

Select the execution scope based on the user's task instructions:

Scope 1: Targeted (Specific File, Directory, or Class)

When the user references a discrete component (e.g., "check complexity in lib/src/auth/" or "audit order_service.dart"), pass explicit targets:

dart run cognitive_complexity@^0.2.4 --threshold 15 lib/src/auth/

Scope 2: Delta (Pull Request, Branch, or Pre-flight Audit)

When reviewing a feature branch, active commit stack, or PR, avoid full-project scanning. Isolate evaluation strictly to modified declarations against trunk:

dart run cognitive_complexity@^0.2.4 --git-diff origin/main --fail-threshold 15 --fail-on-increase

With both flags set, only increases that exceed the threshold fail — healthy sub-threshold increases (e.g. added error handling) are reported without blocking. Omit --fail-threshold only when a strict any-increase ratchet is explicitly desired.

Scope 3: Whole-Project (Default Naked Invocation)

When invoked without targeting parameters ("scan my project for complexity" or /cognitive-complexity), audit the standard source and test roots:

# Production logic target (threshold 15)
dart run cognitive_complexity@^0.2.4 --threshold 15 lib/

# Test harness target (threshold 40)
dart run cognitive_complexity@^0.2.4 --threshold 40 test/

3. Actionable Thresholds & Calibration

  • Production Logic Functions: Target score <= 15. Functions exceeding 15

points mandate architectural refactoring.

  • Test Methods (_test.dart): Target score <= 40. Test suites tolerate

higher setup sequences before decomposition is required.

  • Class Size Ceiling: Logic classes (services, domain objects, controllers)

should remain <= 150 non-comment lines.

  • Flutter UI Calibration: Do not enforce the 150 LOC class ceiling on

declarative Flutter build methods, as widget wrappers consume vertical space without increasing cognitive logic load. Instead, enforce a Widget Tree Nesting Ceiling of maximum 5 horizontal indentation levels before extracting discrete helper widget classes.


4. The Triage & Confirmation Protocol (Audit Before Action)

Discovering high-complexity functions during an audit does not grant permission to autonomously refactor the entire repository. To prevent unwanted diff bloat and preserve historical code stability, adhere to a strict 2-stage workflow:

Stage 1: Read-Only Audit & Reporting (Mandatory Stop)

When threshold breaches are detected, do not mutate code immediately. Output a ranked Markdown Complexity Triage Report directly in chat (or to an artifact for extensive findings) containing:

  • Flagged function name and clickable file local path.
  • Current complexity score versus operational ceiling (sorted descending by

score).

  • Recommended refactoring strategy (Pattern A, B, D, E, or a Pattern C tier) and

unit test status.

Invariant: Outlier-First Mandate

  • Target the Primary Outlier: When remediating complexity, prioritize the

highest-scoring declaration in the report (e.g. Score >= 25 or top outlier) to achieve the highest measurable reduction.

  • Prioritize High-Impact Reductions: Avoid selecting only minor

sub-threshold helpers while leaving severe complexity outliers unaddressed.

  • Decomposition Target: Apply Dart 3 pattern matching, guard clauses, and

method decomposition targeting a post-refactoring score of <= 15. For massive legacy functions (score > 60), safe phased decomposition across isolated PRs is permitted if a single pass would exceed reviewable diff limits.

Stage 2: Interactive User Selection (Confirmation Gate)

Pause execution and prompt the user (via interactive choice or chat) to select the desired sequencing:

  1. (Recommended) Refactor Primary Outlier First: Target the single

highest-scoring declaration in the report, decompose towards score <= 15, verify via unit tests, and present diffs cleanly.

  1. Selective Batch Refactor: Remediate the top N highest-scoring functions

in descending order.

  1. Report-Only / Exit: Acknowledge complexity scores without code mutation.

Explicit Bypass & Non-Interactive Fallback:
- Direct Directives: Skip Stage 1 triage if given an explicit remediation
directive upfront (e.g., "Refactor processOrder in lib/src/order.dart to
fix complexity" or "Refactor the top complexity outlier").
- Non-Interactive Execution: In unattended or automated evaluation
workflows (e.g. evalin or subagents), proceed with Option 1 (Refactor
Primary Outlier) automatically after verifying baseline tests pass.


5. Pre-Refactoring Assessment & Test Coverage Gate

High cognitive complexity strongly correlates with brittle, untested legacy logic. Before undertaking structural refactoring on flagged functions, enforce this verification baseline:

  1. Test Harness Mapping: Confirm an accompanying unit test file exists for

the target declaration (e.g., lib/src/foo.dart -> test/foo_test.dart).

  1. Coverage Audit & Execution:

- If the dart-collect-coverage companion skill is available in your agent runtime, invoke it to check line and branch coverage on the targeted declarations. - At minimum, execute the relevant test suite (dart test test/footest.dart or flutter test test/footest.dart) to confirm a passing green regression baseline before touching code.

  1. Low-Coverage Safety Gate: If tests are missing or coverage around the

flagged function is inadequate: - Interactive Sessions: Pause execution and warn the user. Offer options to (1) write unit tests first, or (2) proceed with surgical refactoring. - Automated Task Execution: Log a low-coverage warning and author a minimal regression test before modifying complex logic.


6. Dart Refactoring Patterns

When remediation is required for declarations flagged by the scanner, apply these Dart-specific architectural refactorings:

Pattern A: Replace Nested If-Else with Dart 3 Switch Expression

In Dart 3, an entire exhaustive switch expression incurs a single base penalty, regardless of how many pattern arms it contains. Converting deeply nested if-else trees into declarative tables removes repeated branching penalties and flattens nesting.

Before: Nested Conditional Ladders (Score: 11)

int resolveTimeout(String protocol, bool isSecure, int retryCount) {
  if (protocol == 'http') {
    if (isSecure) {
      if (retryCount > 3) {
        return 5000;
      } else {
        return 3000;
      }
    } else {
      return 1000;
    }
  } else if (protocol == 'ftp') {
    return isSecure ? 10000 : 2000;
  }
  return 0;
}

After: Table-Driven Switch Expression (Score: 1)

int resolveTimeout(String protocol, bool isSecure, int retryCount) =>
    switch ((protocol, isSecure, retryCount)) {
      ('http', true, > 3) => 5000,
      ('http', true, _) => 3000,
      ('http', false, _) => 1000,
      ('ftp', true, _) => 10000,
      ('ftp', false, _) => 2000,
      _ => 0,
    };

Pattern B: Guard Clause Inversion (Flattening Nesting Depth)

Invert conditional checks into early guard return statements (if (!condition) return;). Every early exit strips away a layer of nesting multiplication from subsequent downstream logic.

Before: Pyramid of Nesting (Score: 11)

Future<void> syncPayload(User? user, Payload? data) async {
  if (user != null) {
    if (user.hasPermission) {
      if (data != null && data.isValid) {
        for (final item in data.items) {
          await repository.save(item);
        }
      }
    }
  }
}

After: Early Exit Guard Clauses (Score: 5)

Future<void> syncPayload(User? user, Payload? data) async {
  if (user == null || !user.hasPermission) return;
  if (data == null || !data.isValid) return;

  for (final item in data.items) {
    await repository.save(item);
  }
}

Pattern C: The 3-Tier Decomposition Rubric (Anti-Goodhart)

Anti-Goodhart Guardrail: Do NOT blindly wrap monolithic functions in single-use runner classes with mutable instance variables just to bring scores below 15. Reducing the metric must never sacrifice transparent data flow or hide bugs.

Deterministic 3-Tier Selection via data_flow

Do not manually estimate or guess which tier a candidate slice belongs to. Run the companion statement-level data-flow analyzer (on-demand form; same SDK 3.12.0+ requirement as the scanner) on each line slice you intend to extract:

dart run cognitive_complexity:data_flow@^0.2.4 lib/src/my_file.dart:45-80

Its report (inputs, mutations, live outputs, control-flow escapes, and a synthesized Dart 3 record signature) selects the tier:

  1. Tier 1 — Pure Functional Decomposition (First Choice):

- Selection: Cleanly extractable slice with 2+ live outputs. - Idiom: Extract a static or top-level function returning the synthesized Dart 3 named record signature verbatim (final (:data, :errors) = stepOne(input);). - Dataclass Boundary: Private, file-local slices use named records at ANY output count—do not create single-use XxxResult dataclasses for them. Reserve dedicated dataclasses only for values that cross public API boundaries or require specialized invariants/methods. - State Scoping: If the helper does not read or mutate class instance state (this), declare it as a private top-level function (or static method) to guarantee referential transparency.

  1. Tier 2 — Standard Helper Extraction (Second Choice):

- Selection: Cleanly extractable slice with <= 1 live output and <= 3 inputs. - Idiom: Extract a standard private helper method or private top-level function returning that single value.

  1. Control-Flow Escapes & Loop Bodies:

- Apply natural seams: enlarge the slice to include the entire enclosing loop or state machine and re-run data_flow. - If the loop body itself is the hotspot, extract it with an explicit signal return (Pattern E)—never a shouldBreak boolean flag. - Use guard-clause inversion (Pattern B) when the escape exists only to skip nested conditions.

  1. Tier 3 — Encapsulated Method Object (Last Resort):

- Mutation-Web Check Gate: Permitted ONLY if data_flow reports on at least two distinct candidate slices show that the intersection of their mutations variable names contains 3 or more entries (the same mutable variables thread through every candidate extraction). - Mechanics: Read [references/method-object.md](references/method-object.md) for extraction mechanics and mandatory idioms. Do not load or apply it speculatively.

Domain-Modeling Exit: When the same tightly coupled mutable state keeps resurfacing across a function (a parser's buffer + cursor, a traversal's queue + visited), the code may be asking to become a real, cohesively named domain class (Parser, GraphTraversal) with a public, unit-tested API. To qualify for the exit, the class MUST have cohesive entity state, more than one public behavior, and its own dedicated test suite. Single-use private facades with one run() method remain subject to the Tier 3 gate.


Pattern D: Fast-Fail Type Matching & Silent Data Swallowing

When refactoring loops and type checks to reduce branching, never replace explicit type casts with pattern matching that silently drops data.

Flawed Structure (Silent Failure):

for (final raw in rawTasks) {
  // SILENTLY DROPS malformed data if raw is not a Map
  if (raw case final Map<String, dynamic> taskMap) {
    _applyTask(taskMap);
  }
}

Correct Structure (Fast-Fail Preservation):

for (final raw in rawTasks) {
  if (raw is! Map<String, dynamic>) {
    errors.add('Malformed task item (expected Map, got ${raw.runtimeType}): $raw');
    continue;
  }
  _applyTask(raw);
}

Pattern E: Loop-Body Extraction with Signal Returns

When a loop body must be extracted but contains break/continue targeting the loop, never smuggle the control flow through boolean flags (shouldBreak soup) — that raises cognitive load and hides termination conditions. Return an explicit signal and keep the loop keywords at the loop site:

enum _ScanAction { proceed, skip, halt }

// Pure, independently testable helper.
_ScanAction _classify(Entry entry, Set<String> seen) {
  if (seen.contains(entry.id)) return _ScanAction.skip;
  if (entry.isTerminal) return _ScanAction.halt;
  return _ScanAction.proceed;
}

outer:
for (final entry in entries) {
  switch (_classify(entry, seen)) {
    case _ScanAction.skip:
      continue;
    case _ScanAction.halt:
      break outer;
    case _ScanAction.proceed:
      process(entry);
  }
}

Use a sealed class instead of an enum when the signal must carry a payload. Asynchronous classification works identically: switch (await classify(entry, seen)). The exhaustive switch keeps every termination path visible at the loop site. Prefer extracting the entire loop when dataflow shows it forms a natural seam; use Pattern E when the loop body alone is the hotspot.


7. Verification Guardrails

Run these verification commands before committing refactored code:

  1. Complexity Audit: Run

dart run cognitive_complexity@^0.2.4 --fail-threshold 15 <refactored files> scoped to the files you touched. Pre-existing breaches elsewhere in the project do not invalidate the refactor.

  1. Code Presentation: Run dart format . (or flutter format .) to

maintain uniform syntactic styling.

  1. Static Analysis: Run dart analyze (or flutter analyze) to ensure zero

static warnings, lint violations, or un-awaited asynchronous gaps.

  1. Test Fidelity:

- Check pubspec.yaml: if sdk: flutter is declared, run flutter test; otherwise run dart test. - In multi-package workspaces, run tests across all dependent packages.


8. Pull Request & Commit Provenance Protocol

When staging refactored code and preparing a commit message or Pull Request:

1. User Confirmation Gate

  • Interactive Sessions: Before writing the PR description or commit body,

explicitly prompt the user in chat or via the harness confirmation tool (e.g. ask_question) whether to include a Tool Provenance & Complexity Delta block.

  • User Prompt Inclusion: When the user explicitly requests inclusion (or

confirms via prompt), append the standardized markdown block below. In unattended or automated workflows, output the summary to chat or step summaries rather than modifying commit bodies without user confirmation.

2. Standardized Provenance Block Format

When confirmed by the user, include the following markdown block in the PR description or commit body so reviewers understand where the changes originated, see the quantified readability improvements, and can rerun the audit locally:

````markdown

🤖 Tool Provenance & Complexity Delta

This refactoring was guided by cognitivecomplexity (v{version}).

<!-- mdformat off(prevent table wrapping) -->

Target Declaration Pre-Score Post-Score Operational Ceiling
{declarationname} ({filepath}) {pre_score} {post_score} <={threshold}

<!-- mdformat on -->

To reproduce or re-evaluate cognitive complexity scores:

{exact_command_line}

<!-- If statement data-flow analysis was used during decomposition: -->

dart run cognitive_complexity:data_flow@^0.2.4 {file}:{start_line}-{end_line}

````

3. Version Resolution

Determine the package version dynamically:

  • Check pubspec.lock in the workspace or run

dart run cognitive_complexity@^0.2.4 --version.

  • If invoked with a specific version constraint (e.g.

cognitive_complexity@^0.2.4), use that exact version.