Features
Fourteen things nobody reviews in a diff.
Each one states what it detects, why it matters, and what it prints. Nothing here describes a capability the tool does not have.
01
Codebase Health
- What it detects
- Six scores over the whole project — architecture, maintainability, complexity, dependencies, type safety, and an overall figure weighted from them.
- Why it matters
- A score is a summary, not a verdict. Its job is to make drift between two runs of the same project visible, and every point of movement can be traced back to a concrete count.
little-owl checklittle-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.
02
Architecture Analysis
- What it detects
- Circular dependencies via Tarjan's algorithm, inverted layer dependencies, layers reaching past their neighbour, cross-feature imports, and edges you explicitly forbade.
- Why it matters
- Cycles make modules impossible to load, test or reason about on their own, and they spread. Layer breaks are how a UI file ends up talking straight to the database.
little-owl architectureExample output
ui imports infrastructure directly
found: ui -> infrastructure
expected: ui -> application -> infrastructure03
Complexity
- What it detects
- Oversized files, functions and React components, cyclomatic complexity, deep nesting and long parameter lists — measured from a real syntax tree, not a line count.
- Why it matters
- Every extra branch is another path a change can break and another case a test has to cover. Components get their own budget, because they are legitimately longer than functions.
little-owl check --detailsExample output
<CheckoutPage> is 486 lines
applyTieredDiscount() has a complexity of 22
→ Flatten early returns, or move each branch group into its own function.04
Dependency Hygiene
- What it detects
- Packages declared but never imported, packages imported but never declared, packages in both dependency lists, major version jumps and newly added dependencies.
- Why it matters
- Each dependency is code you ship, update and trust. Build tooling that legitimately never appears in an import is filtered out, so the list is short enough to act on.
little-owl dependenciesExample output
14 declared 11 imported
ℹ Declared but never imported (may be used via config or at runtime)
lodash05
Change Review
- What it detects
- 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.
- Why it matters
- This is the difference between a linter and Little Owl. Pre-existing debt is not repeated at you every run — only what this change introduced.
little-owl review --scope 'features/orders/**'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
06
Impact Analysis
- What it detects
- Everything that imports a file, directly or through a chain, ranked by distance — plus the routes, tests and external packages involved.
- Why it matters
- Before you change a shared module you get to see its blast radius. It is reachability, not proof, and it says so; a dynamic import it cannot resolve lowers its own confidence.
little-owl impact src/lib/auth/actions.tslittle-owl impact src/lib/auth/actions.ts
$ little-owl impact src/lib/auth/actions.tsCHANGE IMPACT
Changed
src/lib/auth/actions.ts
Potentially affected
HIGH (4 files)
src/app/login/page.tsx
src/components/panel/AdminGate.tsx
src/components/panel/PanelMenu.tsx
src/components/ui/LogoutButton.tsx
MEDIUM (4 files)
src/app/(app)/panel/layout.tsx
src/app/(app)/panel/metrics/page.tsx
src/app/admin/layout.tsx
Routes
/login src/app/login/page.tsx
/panel src/app/(app)/panel/layout.tsx
/panel/metrics src/app/(app)/panel/metrics/page.tsx
/admin src/app/admin/layout.tsx
These files import the change directly or
indirectly. That makes them worth testing — it
does not mean they are broken.
07
Dead Code
- What it detects
- Files nothing in the project reaches, plus exported names nothing imports — each graded high, medium or low confidence, with framework entry points excluded.
- Why it matters
- Static reachability cannot see everything: dynamic imports, plugin registries and file conventions all keep code alive without an import. So it reports candidates with caveats, and never deletes anything.
little-owl dead-code --min-confidence highlittle-owl dead-code
$ little-owl dead-codeDEAD CODE
5 candidates — nothing in the project imports them.
MEDIUM CONFIDENCE (2)
public/sw.js 158 lines
✓ nothing in the project imports it
✓ it exports nothing
⚠ but dynamic imports could not
be resolved
LOW CONFIDENCE (3)
src/components/Mascot.tsx 23 lines
✓ nothing in the project imports it
⚠ but it exports 2 names
⚠ but dynamic imports could not
be resolved
Little Owl never deletes anything.
08
Test Gaps
- What it detects
- Modules with real logic that no test file reaches through imports, and modules a test reaches without naming every exported behaviour.
- Why it matters
- The question is not what your coverage percentage is — a coverage tool answers that better. It is whether anything is watching the behaviour you just changed.
little-owl tests --changedlittle-owl tests --changed
$ little-owl tests --changedTEST GAPS
Limited to the 12 files in the current change.
50 test files reaching 48 modules
✗ NO TEST REACHES THESE (3)
src/lib/pricing/discount.ts
applyTieredDiscount() has 14 branches
src/lib/auth/actions.ts
signIn() has 9 branches
⚠ PARTIALLY COVERED (1)
src/services/orders.ts
reached by src/services/orders.test.ts
no test names: cancelOrder, refundOrder
Little Owl never generates tests. It points at
the gap and stops.
09
Code Archaeology
- What it detects
- The commit that introduced a file, the messages that record a reason, who maintains it, what changes alongside it, and what imports it today.
- Why it matters
- Every answer comes from evidence already in your repository. Where the history is silent it says so, rather than filling the gap with a plausible story.
little-owl explain src/lib/auth/actions.tslittle-owl explain src/lib/auth/actions.ts
$ little-owl explain src/lib/auth/actions.tsCODE ARCHAEOLOGY
src/lib/auth/actions.ts
Evidence: strong
Introduced in 0532b4c (29 days ago).
It has been touched in 11 commits.
4 files import it today.
Commits that explain why
• fix: getCurrentProfile fell open (f1de4da)
• fix: superadmin owner settings (874242e)
Maintained by
A. Rivera (11 commits)
Little Owl only reports what the repository
records. Where the history is silent it says so
rather than guessing at a reason.
10
Project Map
- What it detects
- Areas and their size, detected layers, entry points with route paths, the modules most files depend on, and external services grouped by what they are.
- Why it matters
- Listing every directory alphabetically would be accurate and useless. The ordering principle is what you would read first.
little-owl maplittle-owl map
$ little-owl mapPROJECT MAP
247 files, 37,816 lines, 677 internal imports
START HERE
1. src/app/
2. src/components/
3. src/lib/
Layers
ui
↓
application
↓
infrastructure
Areas
src/lib 80 files, 12,862 lines ←211
src/components 65 files, 10,806 lines ←120
src/app 34 files, 6,861 lines
Most depended on
src/lib/db/schema.ts 39 dependents
src/lib/auth/session.ts 20 dependents
src/lib/format.ts 14 dependents
11
AI Structural Patterns
- What it detects
- 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.
- Why it matters
- These shapes appear whenever changes are made without seeing the whole codebase — which is most of what AI-assisted editing does. Little Owl reports the shape. It never claims to know who wrote the code.
little-owl checkExample output
formatCurrency() is implemented in 3 places
src/lib/format.ts
src/components/cart/price.ts12
Baseline & Drift
- What it detects
- The difference between now and the state you declared healthy — per score, with the counts that explain each move.
- Why it matters
- Little Owl never refreshes the baseline on its own. A baseline that follows the code downhill would quietly redefine healthy as whatever the code is today, and steady degradation would become invisible.
little-owl baselineExample output
Architecture 91 → 84 ↓
Since the baseline: +2 circular dependencies, +3 skipped-layer imports13
Watch Mode
- What it detects
- Drift as it happens, grouped by whether a new finding is in the file you just saved, in something that imports it, or somewhere unrelated.
- Why it matters
- It measures against a fixed reference rather than the state a second ago, and it will not blame your last keystroke for a problem in a file you have not touched.
little-owl watchExample output
Changed
src/services/orders.ts
3 files import this, directly or indirectly14
CI
- What it detects
- Whether the change under review introduced new problems, with a configurable severity gate and a maximum acceptable score drop.
- Why it matters
- By default only new findings can fail a build, so a project with existing debt can adopt Little Owl without fixing everything first.
little-owl ci --base origin/mainExample output
little-owl: DEGRADED
result: fail (1 error-level finding)
$ echo $? → 1Languages
Not every feature reaches every language.
Anything driven by the import graph or by git works the same in all four. The rules that need a real syntax tree do not.
| Capability | TS | JS | Py | Go |
|---|---|---|---|---|
| Dependency graphGo resolves at package level | supported | supported | supported | supported |
| Circular dependencies | supported | supported | supported | supported |
| Layers and boundariesDriven by directories, so language-agnostic | supported | supported | supported | supported |
| File and function size | supported | supported | supported | supported |
| Cyclomatic complexityKeyword-counted outside TS/JS | supported | supported | supported | supported |
| Duplicate blocksTextual, so it works anywhere | supported | supported | supported | supported |
| Impact and reverse deps | supported | supported | supported | supported |
| Code archaeologyReads git, not code | supported | supported | supported | supported |
| Project map | supported | supported | supported | supported |
| Dead codeConfidence is capped below high for Python and Go | supported | supported | supported | supported |
| Test gapsRecognises test_*.py and *_test.go | supported | supported | supported | supported |
| React component budgets | supported | supported | not supported | not supported |
| Type-safety rulesNeeds types to exist | supported | not supported | not supported | not supported |
| Dependency hygieneReads package.json only | supported | supported | not supported | not supported |
| Structural pattern rulesGo exports are functions only | supported | supported | supported | not supported |
Full detail, including where Python and Go stop seeing things, is in the documentation.
See it on your own project
Nothing to configure. It infers your structure on the first run and tells you when it is guessing.
Get Started