Checking the environment
sqlens:doctor answers one question: what can SQLens actually see from here? It is the
command to run first on a new machine and the one to run when two machines disagree about
a report.
It is read-only in the strictest sense the package can offer — no write, no lock, and no query beyond a handshake.
php artisan sqlens:doctor
What it reports
php: 8.4.3
laravel: 13.26.1
sqlens: 0.1.0
os: Darwin
connection pgsql: driver=pgsql server=undetermined (no connection is open — pass --probe to open one)
tool squawk: not_found — 17 check(s) nobody is running. It would add: PostgreSQL migration lint rules for locking, rewrites and unsafe column changes
tool pgls: not_found — 6 check(s) nobody is running. It would add: PostgreSQL schema security checks for RLS gaps, mutable function search paths and extension placement
Four kinds of line:
- The runtime — PHP, Laravel, the SQLens version, the OS family. These are the reproducibility parameters every report carries, printed once where you can read them.
- A line per configured connection — its driver and its real server version.
- A line per optional external tool — its version, or its absence.
- With
--strict, a verdict — would a strict-tools run fail on what is missing here?
--probe: by default it opens nothing, and says so
php artisan sqlens:doctor # opens nothing; every server version is undetermined
php artisan sqlens:doctor --probe # the default connection
php artisan sqlens:doctor --probe=pgsql
php artisan sqlens:doctor --probe=all
Without --probe the command reads whatever handle is already open, and in a fresh CLI
process nothing is. So every connection comes back undetermined — which is honest, and
was also useless: a healthy setup and a broken one produced identical output from the one
command that exists to tell them apart. The default now names the switch that answers the
question.
--probe is scoped to the default connection unless you widen it, and that scope was
measured rather than chosen for tidiness. A stock config/database.php feeds one
DB_PORT to every entry, so probing all of them points the MySQL driver at a PostgreSQL
port — where the TCP session establishes and the handshake never arrives. --probe=all
is right when your connections genuinely differ; it is a slow way to learn nothing when
they do not.
A version that cannot be had is reported as undetermined with a named reason, never as a
silent "ok".
--strict: ask the question a pipeline asks
php artisan sqlens:doctor --strict
strict_tools turns a missing external tool from a degradation into an error. Turning it
on in CI without knowing what is installed there is how a pipeline goes red on its first
run for a reason that has nothing to do with the code in the pull request.
--strict answers that in advance: would a strict-tools run fail on what is missing
here? Run it once on the machine that will run the gate.
The tool lines report a cost, not a status
An optional tool is an amplifier. SQLens reaches every one of its own verdicts without Squawk and without the Postgres language server; a tool that is present adds findings SQLens cannot reach alone.
So the line for an absent tool does not say not_found and stop. It counts the checks
nobody is running and names what they would have covered. A diagnostic that reports an
absence without naming its cost invites a shrug — and the only reason to mention an
optional tool at all is that its absence is otherwise invisible in a report that simply
contains fewer findings.
--format=json
php artisan sqlens:doctor --format=json
Two formats, and only two: console and json.
That is deliberately its own pair rather than the four the other commands accept. doctor
reports the environment, not findings — there is no result for a GitHub annotation or
a SARIF log to describe, so accepting those formats would be a promise it cannot keep.
Exit code
sqlens:doctor reports; it does not gate. It returns 0 for a reachable environment and
uses the ordinary misconfiguration code when what it was asked
to inspect does not exist — an unknown connection name, an unknown format.
An undetermined server version is not a failure here. Not being able to read a version is
information, and this command's job is to hand it to you.