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.View on npm
little-owl check
$ little-owl check🦉 Little Owl
✓ storefront — Next.js · TypeScript
✓ Next.js, React detected
✓ 247 files — 209 source, 38 test
✓ Git repository — change reviews will work
✓ Read the project 247 files
✓ Mapped how files connect 677 connections
✓ Checked the architecture 3 layers: ui → application → infrastructure
✓ Checked for common problems 79ms
────────────────────────────────────────────────────────
Health 81 / 100 ████████████████░░░░
Your project needs attention.
🔴 1 critical Fix before your app goes live.
🟠 6 important Fix soon — this gets more expensive the longer it waits.
🟡 12 minor Improve when you have time. Nothing is broken.
Start with the 1 critical issue. The rest can wait.
WHERE TO START
🔴 #1 A secret can reach the browser through this component
src/components/panel/AdminGate.tsx
Following the imports out of AdminGate.tsx leads to code
that reads a password or key from your environment — and
everything on that path is sent to the browser.
🟠 #2 ui imports infrastructure directly
src/app/panel/config/page.tsx:17
page.tsx reaches straight past the level directly below
it and talks to the one after that.
… and 16 more. Run `little-owl check --all` to see every one.
NEXT STEP
→ little-owl explain 1 the full story of the first issue
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
↓ -6
- 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
Sixteen 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 architectureClient/Server Boundary
Secrets and server-only code that a browser component can reach through a chain of imports — not just by importing them directly. Little Owl walks the real import graph, so it sees the leak that no single file reveals.
little-owl explain 1Change 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.tsTest 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
Client / server boundary
The leak that no single file reveals.
A component imports a helper. The helper imports the database module. The database module reads a key. Nothing in any one file is wrong — and everything on that path is compiled into a page any visitor can open and read.
Little Owl already holds the import graph, so it can answer the question a file-at-a-time linter structurally cannot: from this browser component, is there any route to code that was never meant to leave the server? The finding names the exact chain, the variable, and the fact that a deployed secret has to be rotated as well as removed.
It stays quiet about the things that only look like this. A "use server" module is called over the network, not bundled — that is the correct fix, so recognising one matters as much as finding the leak. Type-only imports and NEXT_PUBLIC_ variables are left alone for the same reason.
🔴 1 critical Fix before your app goes live.
WHERE TO START
🔴 #1 A secret can reach the browser
through this component
components/Profile.tsx
components/Profile.tsx
↓
lib/user.ts
↓
lib/db.ts
reads process.env.DATABASE_URL
NEXT STEP
→ little-owl explain 1Architecture
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.
Everything it reports is numbered, and the number means the same thing in every command. You never have to describe a finding to Little Owl — you point at it.
- 1Your assistant makes a change
- 2little-owl review reports what it did to the project
- 3little-owl explain 1 says what the first issue means
- 4little-owl fix 1 writes a brief precise enough to hand back
- 5little-owl verify 1 confirms the fix actually landed
little-owl review
$ little-owl review🦉 Little Owl
Looking at what changed…
✓ 12 files changed +486 -73, 3 areas
uncommitted changes vs HEAD
✓ Compared with the baseline recorded 2 days ago
Health 89 → 83 ↓ -6
This change introduced something that needs fixing before release.
🔴 1 critical Fix before your app goes live.
🟠 3 important Fix soon — this gets more expensive the longer it waits.
✓ 2 earlier issues no longer appear.
WHAT THIS CHANGE INTRODUCED
🔴 #1 Circular dependency across 3 files
src/services/orders.ts
Some of your files depend on each other in a loop:
orders.ts -> users.ts -> auth.ts -> orders.ts.
Each one needs the other to load first.
… and 2 more. Run `little-owl review --details` to see every one.
NEXT STEP
→ little-owl explain 1 what this issue actually means
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