synerdy/laravel-inspector

Tells you whether a Laravel app is still a clean install, and lists every file the developer added on top of it.

Maintainers

Package info

github.com/Synerdy/laravel-inspector

Homepage

pkg:composer/synerdy/laravel-inspector

Transparency log

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.0.1 2026-08-29 23:02 UTC

This package is auto-updated.

Last update: 2026-08-29 23:07:58 UTC


README

English · Polski

tests

Answers one question: is this Laravel app still a clean install, or did I already build something in it?

You spin up an app in Herd, come back three months later, and cannot remember whether it was a scratch install or the one with the admin panel. Install this package, run one command, and get a list of every controller, model, view, migration, route and test that exists on top of the stock Laravel skeleton.

Dependencies are never scanned. vendor/ and node_modules/ are pruned before they are descended into, so the command finishes in milliseconds even on a large app.

Installation

composer require --dev synerdy/laravel-inspector

The service provider is auto-discovered. Publishing the config is optional:

php artisan vendor:publish --tag=inspector-config

Usage

php artisan inspect
  Laravel Inspector

  Path         C:/Users/marcin/Herd/modsxtest1
  Laravel      13.0.0
  Starter kit  Livewire starter kit (livewire/flux ^2.13.1)
  Scanned      app, bootstrap, config, database, public, resources, routes, tests (absent: lang)
  Files        95 scanned, 36 stock, 54 from starter kit, 5 added

   NOT CLEAN  5 file(s) added.

  Views (2)
    resources/views/components/passkey-registration.blade.php 2026-08-24
    resources/views/components/passkey-verify.blade.php 2026-08-24

  Migrations (1)
    database/migrations/2024_01_01_000000_create_passkeys_table.php 2026-08-24

  Published config (1)
    config/modsx.php 2026-08-27

  Frontend sources (1)
    resources/js/passkeys.js 2026-08-24

  Livewire starter kit (livewire/flux ^2.13.1) (54 file(s), not listed -- use --starter-kit-files)

  Not checked
  Only file names are compared, not contents. Edits inside these stock
  files are invisible to this report:
    resources/views/welcome.blade.php
    app/Models/User.php
    app/Providers/AppServiceProvider.php
    bootstrap/app.php
    database/seeders/DatabaseSeeder.php
  routes/web.php was compared and matches the starter kit version.

The three verdicts

Verdict Exit code Meaning
CLEAN INSTALL 0 Bare laravel new. Nothing beyond the skeleton.
CLEAN INSTALL + kit name 0 Created with a starter kit, nothing built on top of it.
NOT CLEAN 1 Files were added, or a stock file was edited.

The middle one is the case that used to be hard to see: an app made with laravel new --livewire ships around 60 files you never typed, and it is still an empty project.

Options

Option Meaning
--json Emit the report as JSON instead of text
--all List every file instead of the first few per category
--limit=N Files listed per category (default 10)
--starter-kit-files List the starter kit's files instead of just counting them

Since the exit code is meaningful, this is scriptable — to find the untouched apps in a folder full of them:

for app in ~/Herd/*/; do
  [ -f "$app/artisan" ] || continue
  if php "$app/artisan" inspect >/dev/null 2>&1; then
    echo "clean:  $app"
  else
    echo "in use: $app"
  fi
done

How it decides

Files: by name

Detection is by file name, against a built-in list of the files that ship with a stock laravel/laravel application. That list is a union across Laravel 9 through 13, so the package never asks which version you are running and never needs the network.

A file whose path is not on that list was put there by somebody. That is the entire rule.

Scanned directories:

app  bootstrap  config  database  lang  public  resources  routes  tests

Never scanned: vendor, node_modules, storage, bootstrap/cache, public/build, public/storage, public/hot, .git, editor folders, log files, SQLite files.

Published package files under resources/views/vendor/ and public/vendor/ are reported — you published them on purpose, so they are part of what you did to the app.

Starter kits: named, counted, collapsed

An app created with Breeze, Jetstream or one of the Laravel 12+ starter kits ships dozens of files you did not type. The kit is identified from composer.json, package.json and marker paths — those two manifests are read only to name the kit and are never reported as application files — and its files are set aside, counted, and shown as a single line instead of flooding the listing.

Attribution is deliberately conservative. Patterns cover subtrees a kit clearly owns (its auth pages, its layouts, its own tests) and leave ambiguous places alone. Showing a kit file as yours is a small annoyance; hiding a file you wrote would be a wrong answer, so the package errs toward showing too much.

routes/web.php: by content

routes/web.php is where the first route of a new project lands, so its content is compared — this is the one stock file whose default is known well enough to check.

The comparison is structural, not byte-for-byte: the file goes through PHP's tokenizer and is stripped of whitespace, comments and imports. Reformatting, CRLF line endings, an added declare(strict_types=1) and a different Laravel version all pass as untouched, while a single added route does not. The known stock forms of Laravel 9–13 and of the starter kits are recognised by name, so a kit's own web.php does not read as your edit.

What it does not do

Every other stock file is compared by name only. A rewritten resources/views/welcome.blade.php, a relationship added to app/Models/User.php, middleware registered in bootstrap/app.php — none of these are reported. The report names these files explicitly under "Not checked" rather than letting you assume they are untouched.

Configuration

return [
    // Directories walked. Everything else in the project root is out of scope.
    'directories' => ['app', 'bootstrap', 'config', 'database', 'lang', 'public', 'resources', 'routes', 'tests'],

    // Merged on top of the built-in ignore list. A leading slash anchors a
    // pattern to the project root.
    'ignore' => [
        'directories' => [],
        'files' => [],
    ],

    // Treated as part of the baseline, on top of the stock Laravel skeleton.
    // Use this for anything else you did not write yourself.
    'expected' => [],

    // true to detect and collapse starter kits, false to list their files as
    // yours, or an array to replace the built-in definitions.
    'starter_kits' => true,

    // Replace the built-in grouping. First matching pattern wins.
    'categories' => null,
];

Patterns accept * (one level), ** (any depth), a trailing / (directory and its contents), and a leading / (anchored to the project root). A pattern with no slash matches a basename at any depth.

Programmatic use

use Synerdy\LaravelInspector\Inspection\Inspector;
use Synerdy\LaravelInspector\Inspection\Verdict;

$report = app(Inspector::class)->inspect(base_path(), app()->version());

$report->verdict;              // Verdict::Clean | CleanWithStarterKit | Modified
$report->isClean();            // bool — false only for Verdict::Modified
$report->addedCount();         // int
$report->categories;           // Category[] — key, label, files
$report->starterKits;          // StarterKit[] — label, package, version, files
$report->starterKitNames();    // string[]
$report->touchedStockFiles();  // StockFileStatus[] — stock files that were edited
$report->toArray();            // the JSON payload

Testing

composer install
vendor/bin/phpunit

License

MIT.