Skip to content
Local, deterministic, open source

Keep your codebase healthy while AI writes your code.

Little Owl is a local, deterministic codebase analyzer that helps developers keep architecture, maintainability and code health under control in AI-assisted development.

npx little-owl-code

Node.js 18.18 or newer. No account, no API key, nothing to configure first.

little-owl check

$ little-owl check
Name            storefront
Stack           Next.js · TypeScript
Package manager pnpm
Files           247
Git             

CODEBASE HEALTH

Overall

  81 / 100
  ████████████████░░░░

Architecture     79
Maintainability  44
Complexity       96
Dependencies     76
Type Safety      98

🟡 87 warnings   🔵 63 notes   (150 total)

FINDINGS

🟡 architecture  ui imports infrastructure directly
   src/app/(app)/panel/config/page.tsx:17

   panel/config/page.tsx imports lib/db/client.ts,
   skipping the application layer. The detected
   structure is ui -> application -> infrastructure.

   found:    ui -> infrastructure
   expected: ui -> application -> infrastructure

Route the call through application
     instead of importing infrastructure here.

The problem

AI changes code well. It does not watch the shape of your project.

Ask for a refactor and twenty files change. Another pass changes thirty more. Every individual diff looks reasonable — and the architecture is somewhere else by Friday.

Your assistant

Changes individual files

Fast, local, and correct in the diff. Each edit reads as reasonable on its own.

  • + src/components/Orders.tsx
  • + src/lib/format.ts
  • + src/services/orders.ts
  • … 17 more
feeds into

Little Owl

Reads the whole project

The shape of the codebase is not visible in any one diff. This is the part nobody was watching.

  • architecture
  • complexity
  • dependencies
  • tests
  • dead code
  • impact
  • health

Abstractions multiply

The same helper gets written a third time, in a third place.

Boundaries dissolve

A component starts importing the database client directly.

The graph knots up

Two modules import each other and nothing can be tested alone.

Scope creeps

Files change that had nothing to do with what you asked for.

How it works

Three ideas, and nothing else to learn.

Baseline
You record what healthy looks like for this project. Little Owl never moves that mark on its own — a baseline that follows the code downhill would hide the very drift it exists to catch.
Scope
You say what the change was supposed to touch. If files outside that area changed, you hear about it.
Drift
Every score movement is traced back to the counts behind it. A number you cannot explain is not worth showing.

After this change

DEGRADED

  • Architecture9184dropped by 7 points
  • Maintainability8787
  • Complexity8471dropped by 13 points
  • Dependencies9594dropped by 1 points
  • Type Safety9187dropped by 4 points
  • Overall8983dropped by 6 points

Since the baseline: +1 circular dependency, +2 skipped-layer imports, +812 lines

What it looks at

Fourteen things nobody reviews in a diff.

Each one reports what it found, why that matters, and what to do about it — with the confidence it actually has.

  • Architecture Analysis

    Circular dependencies via Tarjan's algorithm, inverted layer dependencies, layers reaching past their neighbour, cross-feature imports, and edges you explicitly forbade.

    little-owl architecture
  • Change Review

    What the current git change did: files touched, lines added and removed, how each score moved, which findings are new, and whether the edit stayed inside the area you scoped it to.

    little-owl review --scope 'features/orders/**'
  • Impact Analysis

    Everything that imports a file, directly or through a chain, ranked by distance — plus the routes, tests and external packages involved.

    little-owl impact src/lib/auth/actions.ts
  • Dead Code

    Files nothing in the project reaches, plus exported names nothing imports — each graded high, medium or low confidence, with framework entry points excluded.

    little-owl dead-code --min-confidence high
  • Test Gaps

    Modules with real logic that no test file reaches through imports, and modules a test reaches without naming every exported behaviour.

    little-owl tests --changed
  • AI Structural Patterns

    The same helper implemented in several files, two modules quietly implementing one concept, modules that only forward a call, and directories full of single-use abstractions.

    little-owl check

Architecture

It knows which way your dependencies are supposed to point.

Layers are read from your configuration, or inferred from your directory names and labelled as a guess. Then every import is checked against them: inverted dependencies, layers reaching past their neighbour, features reaching into each other, and cycles found with Tarjan's algorithm.

Type-only imports are excluded, because they are erased at build time and never create a runtime cycle.

Every rule and its default severity
A three-layer dependency graph. Six modules across ui, application and infrastructure. Every dependency points downward except one: Cart.tsx in the ui layer imports client.ts in the infrastructure layer directly, skipping the application layer.UIAPPLICATIONINFRASTRUCTUREpage.tsxOrders.tsxCart.tsxorders.tspricing.tsclient.ts
allowed dependency ui imports infrastructure directly
Concentric rings around one changed file. Four files import it directly, four more are two to three hops away, and eight are further out.edit8 more4 files4 files
Distance from the change decides the level: direct importers are high, two to three hops are medium, the rest are low.

Impact

See the blast radius before you commit.

Walk the reverse dependency graph from any file and get back what could be affected, ranked by how many import hops away it is — plus the routes involved, the tests that reach it, and the external services it talks to.

It is reachability, not proof, and it says so. When a dynamic import cannot be resolved, it lowers its own confidence rather than pretending the answer is complete.

The loop

Review the change, not the codebase.

A linter tells you the same thousand things every run. Little Owl compares against your baseline and shows what this change introduced — so existing debt stays quiet and new problems stand out.

  1. 1Your assistant makes a change
  2. 2little-owl review reports what it did to the project
  3. 3little-owl prompt writes the fix brief from real findings
  4. 4Review again, against the same baseline

little-owl review

$ little-owl review

🦉 CODEBASE REVIEW

12 files changed (uncommitted changes vs HEAD)
+486 -73 lines   across 3 areas

✗ DEGRADED

Architecture     91 →  84 ↓
Maintainability  87 →  87  ·
Complexity       84 →  71 ↓
Dependencies     95 →  94 ↓
Type Safety      91 →  87 ↓
Overall          89 →  83 ↓

Since the baseline: +1 circular dependency
                    +2 skipped-layer imports

🔴 1 critical   🟡 3 warnings

FINDINGS

🔴 architecture  Circular dependency across 3 files
   src/services/orders.ts

   These files depend on each other in a loop.
   Cycles make modules impossible to load, test
   or reason about independently.

   orders.ts -> users.ts -> auth.ts -> orders.ts

Privacy

Your code stays on your machine.

Little Owl reads files from disk, runs git locally to see what changed, and prints to your terminal. That is the entire data flow. There is no network code in the package to audit.

The only thing it writes is a .little-owl/ directory in your project — your config and baseline, which belong in version control, and a local cache, which it adds to .gitignore for you.

  • No source-code uploads

    Your files are read from disk and never leave it.

  • No backend

    There is no service to be down, rate-limit you, or shut off.

  • No authentication

    No account, no login, no seat count.

  • No API key

    Nothing to provision before the first run.

  • No telemetry

    Not opt-out — absent. There is no analytics code to disable.

  • No AI required

    Little Owl never calls a model. It writes prompts; you decide what to do with them.

Languages

Two languages in depth, two more at the surface.

Python and Go are genuinely supported, and genuinely shallower. Pretending otherwise would cost you more than it gained us.

TypeScript

Full AST

.ts .tsx .mts .cts

TypeScript compiler API — full syntax tree

Imports, exports, dynamic imports, path aliases, functions, components, cyclomatic complexity, nesting, `any`, assertions and suppressions.

Rules only this language gets

  • type-safety/explicit-any
  • type-safety/suppression
  • type-safety/unsafe-assertion
  • type-safety/js-in-ts-project
  • next/server-import-in-client
  • react/effect-dependency-risk

JavaScript

Full AST

.js .jsx .mjs .cjs

TypeScript compiler API — full syntax tree

Everything TypeScript gets, minus the type-safety rules that need types to exist. JSX, CommonJS `require` and ESM are all understood.

Rules only this language gets

  • next/server-import-in-client
  • react/effect-dependency-risk

Python

Shallow

.py .pyi

Line and indentation based

Imports and the dependency graph, relative and absolute module resolution, function boundaries by indentation, complexity, module sizes, and three known smells.

Limits: Not a full parse. Re-exports through `__init__.py` are not followed, so a module reached only through a package export can look unreferenced. Decorators, metaclasses and dynamic attribute access are invisible. It will not replace Ruff.

Rules only this language gets

  • python/bare-except
  • python/mutable-default
  • python/global-state

Go

Shallow

.go

Line and brace based

Package clauses, grouped and single imports, module-relative resolution through go.mod, package-level cycles, function boundaries by brace balance, and oversized packages.

Limits: Exports are detected from capitalised functions only, so exported types, constants and variables are invisible to the dead-code and pattern rules. Build tags and generated code are not special-cased. It will not replace golangci-lint.

Rules only this language gets

  • go/ignored-error
  • go/large-package

Keep your codebase healthy while AI writes your code.

One command, no setup. Point it at a project you already have and see what it says.

npx little-owl-code