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-codeNode.js 18.18 or newer. No account, no API key, nothing to configure first.
little-owl check
$ little-owl checkName 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
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
- Architecture91 →84↓dropped by 7 points
- Maintainability87 →87
- Complexity84 →71↓dropped by 13 points
- Dependencies95 →94↓dropped by 1 points
- Type Safety91 →87↓dropped by 4 points
- Overall89 →83↓dropped 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 architectureChange 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.tsDead 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 highTest 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 --changedAI 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 severityImpact
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.
- 1Your assistant makes a change
- 2little-owl review reports what it did to the project
- 3little-owl prompt writes the fix brief from real findings
- 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