owlcorp/cli-json-lint
A command-line JSON linter for PHP and Laravel projects that validates JSON files, catches syntax errors, and reports clear, actionable output. Add it to Composer scripts, local workflows, or CI pipelines to keep configuration files reliable and consistently checked.
Architecture fit. owlcorp/cli-json-lint is not a Laravel package in the conventional sense — there is no service provider, config file, facade, or event surface to bind into the framework. It is a standalone Composer-distributed PHP CLI (Symfony-Console-style) that lints JSON files and returns non-zero exit codes. That means the integration points are process-level: composer.json scripts, CI job steps, Git hooks, and Makefiles. The assessment-pm template's criteria (service provider integration, escape hatch via database/interface contracts, 1.0 API stability signal) largely don't map; the relevant surface is CLI ergonomics and exit-code contract. Treat it as a dev-tooling dependency, not an application dependency — that distinction dominates every risk below.
Integration feasibility. High and low-effort. It installs with composer require --dev, invokes via vendor/bin/..., and has no Laravel, PHP, or framework version coupling beyond the CLI's PHP floor. No application code changes, no migrations, no config publishing. This is essentially the same operational shape as php -l, phpstan, or php-cs-fixer.
Technical risk. The material risks are not integration complexity but ecosystem signals:
composer.json, tsconfig.json, and .vscode/*.json are common JSONC offenders. If the linter rejects JSONC, it will be noisy on real repositories, and the "zero-config" pitch erodes quickly.json_decode one-liners, jq, ajv, prettier --check, composer validate, and IDE/format-on-save all cover adjacent ground. The package's differentiation is a PHP-native, zero-Node, zero-JQ CI story — that is a real but narrow wedge.Key questions to answer before adoption:
1 used uniformly for lint failure, and are tool errors (bad path, permission) distinguishable from lint failures?--ignore support, traversal of node_modules/vendor, symlink behavior.Stack fit. Correct fit is dev-dependency plus CI/script invocation. Concretely:
composer.json require-dev, pinned to a compatible constraint, with the binary invoked from a scripts.lint-json entry.php -l, phpstan, and composer validate.It is the wrong fit for runtime use, API request/response validation, or any workflow that needs the linter programmatically from PHP. There is no PHP API surface to call; if we need syntax validation inside application code, use json_decode with JSON_THROW_ON_ERROR.
Migration path. There is no migration — there is nothing to migrate from. The relevant decision is substitution: replace ad-hoc shell checks (grep-based brace counting, jq invocations in CI, per-repo bespoke scripts) with a uniform lint step. Adopt per-repository, not platform-wide, until the pilot returns signal. The exit path is equally trivial — remove the dev dependency and the script entry — which is the main reason this is a low-stakes experiment.
Compatibility. No framework coupling. Any PHP 8.x service or monorepo can consume it, including non-Laravel projects. This is actually an advantage over package-specific alternatives if we want one lint step across polyglot repos: the requirement is only PHP, which we already have.
Sequencing.
jq/prettier steps until confidence is established.
3.Standardize or roll back (one release cycle). Review upgrade effort and incident count, then either bless it in the platform tooling inventory or remove it and document why.Maintenance. Low absolute cost, non-zero attribution cost. Someone must own: dependency bumps in composer.json, watching for upstream abandonment (0 stars makes this a realistic near-term outcome), and verifying on a PHP minor upgrade that the binary still runs. Because it is dev-only, a stale version cannot affect production traffic — that materially lowers the priority of remediation.
Support. Self-support is the working assumption. No vendor SLA, no commercial tier, no paid escalation path. Documentation appears thin (the description is a marketing paragraph, not a reference), so the pilot owner should write a short internal README: exact invocation, exit-code semantics, and the JSONC decision. That README is the actual asset — it is what makes the tool safe for other squads to self-serve.
Scaling. Batch mode across directories is the advertised use. Runtime is linear in file count and file size; for typical Laravel repos (hundreds of JSON files, mostly small) this is a sub-second CI step. Watch two edge cases at scale: large fixtures directories containing intentionally malformed JSON (test corpora, snapshot testdata) which will need explicit exclusion, and monorepo traversal into node_modules/vendor if default ignore rules are weak. Verify ignore behavior in the pilot before applying to the monorepo.
Failure modes.
.vscode/, tsconfig.json, and known JSONC files if unsupported.Ramp-up. Trivial for individual contributors — one command, one documented invocation. The real ramp-up cost is organizational, not technical: getting squads to add the step consistently, agreeing on exclusions, and resisting the urge for each repo to wrap it in a custom script. Keep the invocation identical across repos or the standardization benefit evaporates.
Bottom line for the decision record. This is a small, permissively licensed, narrowly scoped dev tool with a real but modest niche (PHP-native JSON linting without a Node/JQ dependency). It is not a Laravel package, it is not a v1.0-stability bet in the sense the template assumes, and the build-vs-buy question is really a pick-one-commodity-tool question — we should not build a JSON linter, and we should not standardize on a 0-star project either. Recommend a one-repo, one-release-cycle pilot with an explicit rollback criterion (any JSONC false positive that cannot be excluded, or evidence of upstream stagnation) and no roadmap displacement.
How can I help you explore Laravel packages today?