Skip to content

Quick start

What you need

  • Node.js 22 or newer
  • A repository with git history — that is where change frequency comes from
  • A TypeScript codebase (monorepo or a single package, both work)

It does not work on a shallow clone

With git clone --depth 1, or the default settings of most CI systems, the history is truncated and change frequency, hotspots, and coupling are all wrong. dowse detects this and warns, but the numbers are not trustworthy. In CI, set fetch-depth: 0.

Run it

Nothing to install.

bash
npx dowse analyze

Within seconds you get something like this — about 7 seconds for a monorepo of 2,238 files and 3,920 commits.

  Discovering workspace... 40 packages (pnpm)
  Reading git history (since 12 months ago)... 3920 commits
    excluded: 6 merge, 0 bot, 1 format-only, 0 ignore-revs
  Parsing AST (oxc)... 2238 files
  Building package graph... 40 packages, 168 edges
  Computing change coupling... 500 file pairs, 25 package pairs (1 hidden)

⚠ Top hotspots (complexity=loc):
┌───┬─────────────────────────────┬───────┬────────┬────────┬─────────┐
│ # │ file                        │ score │   freq │     cx │ commits │
├───┼─────────────────────────────┼───────┼────────┼────────┼─────────┤
│ 1 │ services/api/src/runtime.ts │ 0.999 │ 100pct │ 100pct │     255 │
└───┴─────────────────────────────┴───────┴────────┴────────┴─────────┘

📌 Top findings (where to look first):
  🔥 services/api/src/runtime.ts — changed often and complex (255 commits, 857 LoC)
  🔗 @acme/admin-web ↔ @acme/doctor-web — changed together 91 times with no dependency

The full result is also written to .dowse/dowse.json.

Why it prints what it excluded

Every run states that merge, bot, and format-only commits were removed. Excluding them silently would let you read the output as "everything, analysed" — and a number you cannot tell the scope of is not a number you can act on.

Dig into what it found

Why do these change together?

When hidden coupling shows up, look at what is actually behind it.

bash
npx dowse why @acme/admin-web @acme/doctor-web
■ File pairs that change together most
  src/lib/server-api.ts        ↔  src/lib/server-api.ts          13
  src/app/(authed)/page.tsx    ↔  src/app/(authed)/page.tsx      15

■ Representative commits (newest first)
  c23db79c 2026-07-09 add re-login prompt on session expiry to all three surfaces [A:3 B:6 files]

Identically named files on both sides, changed by hand in lockstep. A candidate for extraction into a shared package.

Is that change frequency real?

bash
npx dowse analyze --verify-churn

It compares ASTs to separate real edits from renames and reformatting.

🔍 Churn verification (AST comparison, top 20 files):
  513 of 568 revisions were real changes (55 false: 12 renames / 35 format-only / 8 unchanged)
  ⚠ doctor-call-panel.tsx: 42% of its churn did not change meaning

This runs git show per revision, so it is opt-in and limited to the top N files.

Why is this file complex?

bash
npx dowse health --file src/foo.ts
  health: 4.9 / 10  (lines-of-code baseline: 10.0)
  deductions from 10.0:
    -3.00 high cognitive complexity (50 > threshold 15, severity 1.00)
         → validateEnvironmentConfig (cognitive=50, L3)
    -2.13 complex function (27 > threshold 10, severity 0.85)
         → validateEnvironmentConfig (cyclomatic=27, L3)

The LoC baseline is 10.0 but health is 4.9 — a short but dense file. A metric that only counts lines would miss it entirely.

Where is the knowledge concentrated?

bash
npx dowse knowledge

Bus factor per package, plus what would be left unowned if the main developer left.

Use it from an agent

If you use Claude Code, wiring it up as an MCP server is the most practical setup.

bash
claude mcp add dowse -- npx -y dowse mcp

From then on you can ask "where should I refactor next in this repo?" inside a session and get an answer grounded in the analysis. See Coding agents.

Let it do the fix

bash
npx dowse fix

It hands the top hotspot to an agent, verifies the result with your type checker and tests, and reports how much complexity moved. It is a dry run by default — the change is always discarded.

packages/cli/src/analyze.ts
  cognitive -41 / cyclomatic -22 / LoC -111
  verified: typecheck ✓ / tests ✓

Add --apply to keep it. See Closing the loop.

Next

Released under the MIT License