Skip to content

Closing the loop

Measuring is not the end. Fix, verify, and measure the effect — in one command.

Why measure at all

"Fixing hotspots gives the best return" is a hypothesis. Until you actually fix one and see how far the complexity moved, the claim is untested.

dowse measures complexity before and after, and reports the difference.

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

What you need

Claude Code, installed and logged in.

bash
claude --version

No API key configuration required — it runs on your existing authentication and permission settings. Without Claude Code, an ANTHROPIC_API_KEY is enough for diagnosis only; applying a fix needs a harness that can edit files.

Diagnose only

bash
dowse diagnose --top 3

An LLM reads the top hotspots and returns why they are risky, how to fix them, and roughly what it will cost.

packages/cli/src/analyze.ts
  why risky: five subcommand entry points each re-implement option parsing,
             the JSON/human output split, and table formatting inside one file;
             display logic and command control are not separated
  estimate: medium (half a day)
  plan:
    1. extract couplingFilters and asProxy as pure functions
       makes them testable, so later output changes cannot silently break aggregation
  risks:
    - the CLI's stdout is itself a contract for users; even column order breaks downstream

This is an opinion, not a measurement

Keep it clearly separate from the deterministic metrics (hotspot, health). It is a reading produced by an LLM.

Cost is proportional to the number of files. Control it with --top.

Let it do the fix

bash
dowse fix

Takes the top hotspot through diagnose → fix → verify → measure. You can also name a file.

bash
dowse fix packages/core/src/foo.ts

Dry run by default

The change is always discarded. The run verifies and measures so you can see what fixing it would do.

To keep it, pass --apply.

bash
dowse fix --apply

Changes that pass verification stay on a working branch.

The safety valves

Because this loop rewrites files, several guards are in place.

GuardBehaviour
Uncommitted change detectionRefuses to run if the working tree is dirty
Branch isolationWorks on dowse/fix-<target>-<sha>
Independent verificationdowse runs the type check and tests itself
Diff ceilingDiscards changes larger than 400 lines by default
Dry run by defaultNothing is kept unless you pass --apply

Verification is not left to the agent

An agent reporting "fixed it" is not evidence. dowse runs the type check and tests directly and decides on the exit code.

It works out how to run them from the repository itself — the packageManager field first, then the lockfile, falling back to npm. pnpm, bun, yarn: it uses whatever that repository uses.

Failed changes are discarded and the failure is shown.

packages/foo.ts: discarded — type check failed
  verified: typecheck ✗ / tests ✓
  src/foo.ts(42,10): error TS2322: Type 'string' is not assignable to 'number'.

When there are no verification scripts

In a workspace whose package.json has neither a typecheck nor a test script, nothing can be verified. dowse then refuses to report a pass and discards the change, rather than letting through something it never checked.

Sometimes it changes nothing

If the agent concludes there is nothing worth improving, it leaves the file alone.

packages/core/src/metrics/churn.ts: the agent made no changes
  total: 0 of 1 improved / cost $0.56

That is correct behaviour. Mechanically churning an already-simple file buys nothing.

Reading the result

┌────────────────────┬─────────┬───────────┬────────────┬──────┬───────┬───────┐
│ target             │ outcome │ cognitive │ cyclomatic │  LoC │ diff  │  cost │
├────────────────────┼─────────┼───────────┼────────────┼──────┼───────┼───────┤
│ src/analyze.ts     │ applied │       -41 │        -22 │ -111 │   439 │ $1.86 │
└────────────────────┴─────────┴───────────┴────────────┴──────┴───────┴───────┘
OutcomeMeaning
appliedPassed verification (discarded after measuring on a dry run)
discarded — verificationType check or tests failed
discarded — too largeExceeded the diff ceiling
no changeThe agent judged no improvement was needed
abortedThe working tree had uncommitted changes

Negative numbers are improvements. When nothing improved, that is reported as it is.

Released under the MIT License