@limenet-ch/baseline 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +272 -0
- package/js/dist/bin.d.ts +2 -0
- package/js/dist/bin.js +59 -0
- package/js/dist/bin.js.map +1 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.d.ts +11 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.js +14 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/biome-uses-local-schema-check.d.ts +21 -0
- package/js/dist/checks/biome-uses-local-schema-check.js +78 -0
- package/js/dist/checks/biome-uses-local-schema-check.js.map +1 -0
- package/js/dist/checks/calls-baseline-check.d.ts +11 -0
- package/js/dist/checks/calls-baseline-check.js +34 -0
- package/js/dist/checks/calls-baseline-check.js.map +1 -0
- package/js/dist/checks/check.d.ts +37 -0
- package/js/dist/checks/check.js +54 -0
- package/js/dist/checks/check.js.map +1 -0
- package/js/dist/checks/ci-sets-node-version-check.d.ts +10 -0
- package/js/dist/checks/ci-sets-node-version-check.js +43 -0
- package/js/dist/checks/ci-sets-node-version-check.js.map +1 -0
- package/js/dist/checks/claude-settings-check.d.ts +30 -0
- package/js/dist/checks/claude-settings-check.js +43 -0
- package/js/dist/checks/claude-settings-check.js.map +1 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.d.ts +13 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.js +25 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.d.ts +5 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.js +25 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.js.map +1 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.d.ts +13 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.js +30 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.js.map +1 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.d.ts +5 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.js +68 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.js.map +1 -0
- package/js/dist/checks/has-ci-jobs-check.d.ts +9 -0
- package/js/dist/checks/has-ci-jobs-check.js +14 -0
- package/js/dist/checks/has-ci-jobs-check.js.map +1 -0
- package/js/dist/checks/has-editorconfig-check.d.ts +6 -0
- package/js/dist/checks/has-editorconfig-check.js +42 -0
- package/js/dist/checks/has-editorconfig-check.js.map +1 -0
- package/js/dist/checks/has-npm-scripts-check.d.ts +5 -0
- package/js/dist/checks/has-npm-scripts-check.js +20 -0
- package/js/dist/checks/has-npm-scripts-check.js.map +1 -0
- package/js/dist/checks/has-trivy-config-check.d.ts +26 -0
- package/js/dist/checks/has-trivy-config-check.js +179 -0
- package/js/dist/checks/has-trivy-config-check.js.map +1 -0
- package/js/dist/checks/is-ci-lint-complete-check.d.ts +10 -0
- package/js/dist/checks/is-ci-lint-complete-check.js +25 -0
- package/js/dist/checks/is-ci-lint-complete-check.js.map +1 -0
- package/js/dist/checks/node-version-check.d.ts +5 -0
- package/js/dist/checks/node-version-check.js +77 -0
- package/js/dist/checks/node-version-check.js.map +1 -0
- package/js/dist/checks/periodic-check.d.ts +16 -0
- package/js/dist/checks/periodic-check.js +34 -0
- package/js/dist/checks/periodic-check.js.map +1 -0
- package/js/dist/checks/registry.d.ts +10 -0
- package/js/dist/checks/registry.js +56 -0
- package/js/dist/checks/registry.js.map +1 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.d.ts +6 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.js +26 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/updates-dependencies-check.d.ts +5 -0
- package/js/dist/checks/updates-dependencies-check.js +8 -0
- package/js/dist/checks/updates-dependencies-check.js.map +1 -0
- package/js/dist/checks/uses-release-it-check.d.ts +12 -0
- package/js/dist/checks/uses-release-it-check.js +40 -0
- package/js/dist/checks/uses-release-it-check.js.map +1 -0
- package/js/dist/commands/check.d.ts +7 -0
- package/js/dist/commands/check.js +59 -0
- package/js/dist/commands/check.js.map +1 -0
- package/js/dist/commands/install-skills.d.ts +16 -0
- package/js/dist/commands/install-skills.js +47 -0
- package/js/dist/commands/install-skills.js.map +1 -0
- package/js/dist/commands/periodic.d.ts +6 -0
- package/js/dist/commands/periodic.js +40 -0
- package/js/dist/commands/periodic.js.map +1 -0
- package/js/dist/index.d.ts +8 -0
- package/js/dist/index.js +9 -0
- package/js/dist/index.js.map +1 -0
- package/js/dist/policy.d.ts +45 -0
- package/js/dist/policy.js +145 -0
- package/js/dist/policy.js.map +1 -0
- package/js/dist/project.d.ts +26 -0
- package/js/dist/project.js +64 -0
- package/js/dist/project.js.map +1 -0
- package/js/dist/state.d.ts +21 -0
- package/js/dist/state.js +36 -0
- package/js/dist/state.js.map +1 -0
- package/js/dist/support/ci-jobs.d.ts +13 -0
- package/js/dist/support/ci-jobs.js +42 -0
- package/js/dist/support/ci-jobs.js.map +1 -0
- package/js/dist/support/npmrc.d.ts +10 -0
- package/js/dist/support/npmrc.js +36 -0
- package/js/dist/support/npmrc.js.map +1 -0
- package/js/dist/support/versions.d.ts +10 -0
- package/js/dist/support/versions.js +34 -0
- package/js/dist/support/versions.js.map +1 -0
- package/js/skills/creating-a-release/SKILL.md +45 -0
- package/js/skills/updating-dependencies/SKILL.md +104 -0
- package/package.json +67 -0
- package/policy/policy.json +131 -0
- package/policy/policy.schema.json +284 -0
- package/policy/templates/editorconfig +18 -0
- package/policy/templates/trivy.yaml +19 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Linus Metzler <hi@linusmetzler.me>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Laravel Baseline
|
|
2
|
+
|
|
3
|
+
[](https://packagist.org/packages/limenet/laravel-baseline)
|
|
4
|
+
[](https://github.com/limenet/laravel-baseline/actions/workflows/run-tests.yml)
|
|
5
|
+
[](https://github.com/limenet/laravel-baseline/actions/workflows/fix-php-code-style-issues.yml)
|
|
6
|
+
[](https://codecov.io/gh/limenet/laravel-baseline)
|
|
7
|
+
[](https://packagist.org/packages/limenet/laravel-baseline)
|
|
8
|
+
|
|
9
|
+
Checks your Laravel installation against a highly opinionated baseline.
|
|
10
|
+
|
|
11
|
+
This repository ships **two runners** from one policy:
|
|
12
|
+
|
|
13
|
+
| | Composer | npm |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Package | `limenet/laravel-baseline` | `@limenet-ch/baseline` |
|
|
16
|
+
| For | Laravel projects (DDEV, composer) | JS/TS-only projects (no PHP, no DDEV) |
|
|
17
|
+
| Command | `php artisan limenet:laravel-baseline:check` | `npx baseline check` |
|
|
18
|
+
| Checks | all of them | [the portable subset](#js-only-projects) |
|
|
19
|
+
|
|
20
|
+
Both read `policy/`, so the version floors and required keys are defined once, and both are
|
|
21
|
+
executed against the shared behavioural fixtures in `fixtures/`. They are released in lockstep:
|
|
22
|
+
the same version number means the same policy in both ecosystems.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
You can install the package via composer:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
composer require limenet/laravel-baseline
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
You can publish the config file with:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
php artisan vendor:publish --tag="laravel-baseline-config"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
Add to your `composer.json` to run checks (and auto-fix) after every `composer update`:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
"post-update-cmd": [
|
|
46
|
+
"@php artisan limenet:laravel-baseline:check --fix"
|
|
47
|
+
],
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Running checks
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Check only — report issues without making changes
|
|
54
|
+
php artisan limenet:laravel-baseline:check
|
|
55
|
+
|
|
56
|
+
# Auto-fix — apply all safe automatic fixes, then report remaining issues
|
|
57
|
+
php artisan limenet:laravel-baseline:check --fix
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Checks marked 🔧** below support `--fix`. When `--fix` is used:
|
|
61
|
+
- Fully fixable checks: all conditions are applied automatically.
|
|
62
|
+
- Partially fixable checks *(requires package installed first)*: configuration/script entries are fixed once the package is installed via `composer require`.
|
|
63
|
+
- Non-fixable checks: report the issue with an actionable message.
|
|
64
|
+
|
|
65
|
+
### AI guidelines & skills
|
|
66
|
+
|
|
67
|
+
The package also ships [Laravel Boost](https://laravel.com/docs/boost) resources under
|
|
68
|
+
`resources/boost/`: an always-on guideline (the dev loop — `ci-lint`, tests, DDEV-first
|
|
69
|
+
conventions) and on-demand skills (e.g. `creating-a-release`). When a project that has
|
|
70
|
+
`laravel/boost` installed runs `php artisan boost:install` or `php artisan boost:update --discover`,
|
|
71
|
+
Boost discovers and publishes these to the consuming project's coding agents automatically.
|
|
72
|
+
|
|
73
|
+
## JS-only projects
|
|
74
|
+
|
|
75
|
+
Projects with no PHP and no DDEV use the npm runner instead. It is a second implementation, not a
|
|
76
|
+
wrapper: PHP cannot reach into a JS project (and by this baseline's own convention `npm` runs on
|
|
77
|
+
the host while artisan runs inside DDEV), so the portable checks are reimplemented in TypeScript and
|
|
78
|
+
kept honest by the shared fixtures rather than by shared code.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install --save-dev @limenet-ch/baseline
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx baseline check # report issues
|
|
86
|
+
npx baseline check --fix # apply safe fixes, then report what is left
|
|
87
|
+
npx baseline periodic # walk through expired periodic checks
|
|
88
|
+
npx baseline install-skills # copy the packaged skills into .claude/skills/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Wire it into the `ci-lint` npm script so CI and the Claude Stop hook both run it — npm has no
|
|
92
|
+
`post-update-cmd` equivalent, and npm 12 blocks dependency lifecycle scripts by default:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
"scripts": {
|
|
96
|
+
"ci-lint": "biome ci . && tsc --noEmit && baseline check"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
State lives in `.baseline.json` at the project root (a JS project has no `config/` directory):
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"excludes": ["hasNpmScripts"],
|
|
105
|
+
"periodic": { "updatesDependencies": "2026-08-16T09:00:00.000Z" }
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### What the npm runner checks
|
|
110
|
+
|
|
111
|
+
| Check | Relationship to the Laravel runner |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `nodeVersion` | identical |
|
|
114
|
+
| `hardensNpmSupplyChain` | identical |
|
|
115
|
+
| `hasEditorconfig` | identical |
|
|
116
|
+
| `biomeUsesLocalSchema` | identical |
|
|
117
|
+
| `doesNotHaveCopilotOrJunieAgentFiles` | identical |
|
|
118
|
+
| `doesNotUseBothBaselineRunners` | mirrored: fails when `composer.json` requires `limenet/laravel-baseline`, since the Composer runner wins |
|
|
119
|
+
| `allowsToolingInClaudeSettings` | requires only the shared allow entries, not the DDEV/artisan ones |
|
|
120
|
+
| `deniesEnvReadsInClaudeSettings` | identical |
|
|
121
|
+
| `runsCiLintHookInClaudeSettings` | hooks `npm run ci-lint` instead of `ddev composer run ci-lint` |
|
|
122
|
+
| `updatesDependencies` | identical (periodic, every 30 days) |
|
|
123
|
+
| `hasNpmScripts` | identical |
|
|
124
|
+
| `hasCiJobs` | same GitLab CI templates, without the `php` job |
|
|
125
|
+
| `hasTrivyConfig` | identical, canonical config included: its `vendor/**`, `storage/logs/` and `.ddev/` skips are inert in a JS project |
|
|
126
|
+
| `ciSetsNodeVersion` | identical |
|
|
127
|
+
| `isCiLintComplete` | asserts the JS toolchain in the npm script, not pint/phpstan in a composer script |
|
|
128
|
+
| `callsBaseline` | hooks the `ci-lint` npm script, since npm has no `post-update-cmd` |
|
|
129
|
+
| `usesReleaseIt` | **inverted**: fails if `@release-it/bumper` is configured, because `package.json` is already release-it's source of truth |
|
|
130
|
+
|
|
131
|
+
Deliberately not ported: everything composer-, artisan-, Rector-, PHPStan- or Spatie-Health-shaped;
|
|
132
|
+
the DDEV checks (`ddevNodeVersionIsAuto`, `ddevMutagenIgnoresNodeModules`, …); and
|
|
133
|
+
`hasClaudeSettingsWithLaravelSkills` / `doesNotHaveLaravelSimplifierInClaudeSettings`, which are
|
|
134
|
+
vacuous without Laravel.
|
|
135
|
+
|
|
136
|
+
## Checks
|
|
137
|
+
|
|
138
|
+
This package validates your Laravel installation against the following checks:
|
|
139
|
+
|
|
140
|
+
### Testing & Quality Tools
|
|
141
|
+
- **`usesPest()`** - Validates Pest testing framework is configured (not PHPUnit directly)
|
|
142
|
+
- **`usesPestPhpstanPlugin()`** - Validates `pestphp/pest-plugin-phpstan` is installed when Pest 5+ and PHPStan are both present (warns if not applicable)
|
|
143
|
+
- **`usesPestRectorPlugin()`** - Validates `pestphp/pest-plugin-rector` is installed when Pest 5+ and Rector are both present (warns if not applicable)
|
|
144
|
+
- 🔧 **`usesRector()`** - Validates Rector automated code modernization is installed *(partial: fixes ci-lint script if packages installed)*
|
|
145
|
+
- **`usesLarastan()`** - Validates Larastan static analysis tool is configured
|
|
146
|
+
- **`usesPhpstanExtensions()`** - Validates PHPStan extensions are installed
|
|
147
|
+
- **`phpstanLevelAtLeastEight()`** - Validates PHPStan is configured to at least level 8
|
|
148
|
+
- 🔧 **`checkPhpunit()`** - Validates PHPUnit configuration with coverage reports *(adds missing XML nodes and APP_KEY)*
|
|
149
|
+
- 🔧 **`hasRectorConfigWithComposerBased()`** - Validates Rector `withComposerBased(phpunit, symfony, laravel)` is configured *(appends call to rector.php)*
|
|
150
|
+
- 🔧 **`hasRectorConfigWithConfiguredRules()`** - Validates Rector `withConfiguredRule()` calls are present for `RouteActionCallableRector` and `WhereToWhereLikeRector` *(appends calls to rector.php)*
|
|
151
|
+
- 🔧 **`hasRectorConfigWithPreparedSets()`** - Validates Rector `withPreparedSets(deadCode, codeQuality, codingStyle, typeDeclarations, privatization, instanceOf, earlyReturn)` is configured *(appends call to rector.php)*
|
|
152
|
+
- 🔧 **`hasRectorConfigWithImportNames()`** - Validates Rector `withImportNames(importShortClasses: false)` is configured *(appends call to rector.php)*
|
|
153
|
+
- 🔧 **`hasRectorConfigWithPhpSets()`** - Validates Rector `withPhpSets()` is called *(appends call to rector.php)*
|
|
154
|
+
- 🔧 **`hasRectorConfigWithAttributesSets()`** - Validates Rector `withAttributesSets()` is called *(appends call to rector.php)*
|
|
155
|
+
- 🔧 **`hasRectorConfigWithSetProviders()`** - Validates Rector `withSetProviders(LaravelSetProvider)` is configured *(appends call to rector.php)*
|
|
156
|
+
- 🔧 **`hasRectorConfigWithRules()`** - Validates Rector `withRules([AddGenericReturnTypeToRelationsRector, MinutesToSecondsInCacheRector, UseForwardsCallsTraitRector])` is configured *(appends call to rector.php)*
|
|
157
|
+
- 🔧 **`hasRectorConfigWithSets()`** - Validates Rector `withSets([LaravelBaselineSetList::REMOVE_DEFAULT_DOCBLOCKS, LaravelSetList::LARAVEL_*])` is configured with all required sets *(appends call to rector.php)*
|
|
158
|
+
- 🔧 **`hasRectorConfigWithPaths()`** - Validates Rector `withPaths([app, database, routes, tests])` is configured *(appends call to rector.php)*
|
|
159
|
+
- 🔧 **`hasRectorConfigWithPestSet()`** - Validates Rector `withSets([PestSetList::CODING_STYLE])` is configured when Pest 5+ and Rector are both present *(appends call to rector.php; warns if not applicable)*
|
|
160
|
+
- 🔧 **`hasRectorConfigWithSkip()`** - Validates Rector `withSkip()` contains required skipped rules (always: 6 Laravel rules; Laravel 13+: TablePropertyToTableAttributeRector; when server.php exists: ServerVariableToRequestFacadeRector) *(appends call to rector.php)*
|
|
161
|
+
|
|
162
|
+
### IDE & Developer Tools
|
|
163
|
+
- 🔧 **`hasEditorconfig()`** - Validates `.editorconfig` exists with required settings (`root = true`, `charset`, `end_of_line`, `indent_style`, `insert_final_newline`, `trim_trailing_whitespace`) *(creates `.editorconfig` with canonical content if missing or incomplete)*
|
|
164
|
+
- 🔧 **`hasClaudeSettingsWithLaravelSkills()`** - Validates Claude Code settings include the Laravel agent skills plugin and marketplace *(creates/merges `.claude/settings.json`)*
|
|
165
|
+
- 🔧 **`doesNotHaveLaravelSimplifierInClaudeSettings()`** - Fails if the deprecated `laravel-simplifier@laravel` plugin is still enabled in `.claude/settings.json` *(removes the entry)*
|
|
166
|
+
- 🔧 **`deniesEnvReadsInClaudeSettings()`** - Validates `.claude/settings.json` `permissions.deny` blocks reading `.env` plus every environment that ships an encrypted file (each `.env.{env}.encrypted` in the project root requires denying `.env.{env}`); `.env.example` stays readable *(merges the deny entries)*
|
|
167
|
+
- 🔧 **`allowsToolingInClaudeSettings()`** - Validates `.claude/settings.json` `permissions.allow` includes the DDEV dev-loop commands (`ddev composer run ci-lint`, `ddev composer test`, and safe artisan commands: `test`, `make:*`, `route:list`, `about`, `config:show`, `ide-helper`, `optimize:clear`, `cache:clear`, `config:clear`, `route:clear`, `view:clear`) so the dev loop runs without prompts *(merges the allow entries)*
|
|
168
|
+
- 🔧 **`asksBeforeDestructiveDbCommandsInClaudeSettings()`** - Validates `.claude/settings.json` `permissions.ask` requires a confirmation before the artisan commands that destroy database contents (`migrate:fresh`, `migrate:refresh`, `migrate:reset`, `migrate:rollback`, `db:wipe`), covering both the `ddev artisan` and `php artisan` forms; `ask` rather than `deny` so the developer keeps an approval path *(merges the ask entries)*
|
|
169
|
+
- 🔧 **`runsCiLintHookInClaudeSettings()`** - Validates `.claude/settings.json` has a `Stop` hook running `ddev composer run ci-lint` *(appends the hook)*
|
|
170
|
+
- 🔧 **`usesIdeHelpers()`** - Validates Laravel IDE Helper is configured: `post-update-cmd` runs `ide-helper:generate`, `ide-helper:models`, and `ide-helper:meta`, and `.gitignore` ignores the generated `_ide_helper.php`, `_ide_helper_models.php`, and `.phpstorm.meta.php` files *(partial: adds post-update scripts and gitignore entries if package installed)*
|
|
171
|
+
- 🔧 **`gitignoresLspFiles()`** - Validates `.gitignore` ignores `storage/framework/lsp-*.php`, the per-editor files the Laravel language server writes into `storage/framework` *(appends the entry, creating `.gitignore` if missing)*
|
|
172
|
+
- **`usesLaravelAdminer()`** - Warns if Laravel Adminer database UI is missing (optional), validates TFA confirmation and configuration when installed
|
|
173
|
+
- 🔧 **`usesLaravelBoost()`** - Validates Laravel Boost AI development tool *(partial: fixes boost.json and post-update script if package installed)*
|
|
174
|
+
- 🔧 **`laravelBoostMcpUsesDdev()`** - Validates `.mcp.json` configures the `laravel-boost` MCP server to use `ddev artisan boost:mcp` *(creates/fixes `.mcp.json`; warns if `laravel/boost` not installed)*
|
|
175
|
+
- 🔧 **`doesNotHaveCopilotOrJunieAgentFiles()`** - Fails if `AGENTS.md`, a `.junie` directory, or a `.github/skills` directory exist — these are generated for the Copilot/Junie Boost agents, which are no longer required *(deletes `AGENTS.md`, the `.junie` directory, and the `.github/skills` directory)*
|
|
176
|
+
- **`runsBoostUpdate()`** *(periodic, every 30 days)* - Warns if Laravel Boost is not installed; when installed, fails until a developer confirms running `php artisan boost:update --discover` via `limenet:laravel-baseline:periodic`
|
|
177
|
+
- **`followsModernLaravelIdioms()`** *(periodic, every 30 days)* - Fails until a developer confirms running the `auditing-laravel-idioms` skill, which audits typed cache getters, BackedEnum cache/session keys, and `whenFilledEnum()` for request data
|
|
178
|
+
- **`updatesDependencies()`** *(periodic, every 30 days)* - Fails until a developer confirms (via `limenet:laravel-baseline:periodic`) that composer & npm dependencies were updated by running the `updating-dependencies` skill — which updates in-constraint packages, reviews changelogs for project impact, and reports semver-blocked majors
|
|
179
|
+
- 🔧 **`usesLimenetPintConfig()`** - Validates custom Laravel Pint formatting standards *(partial: adds post-update script if package installed)*
|
|
180
|
+
|
|
181
|
+
### Laravel Features & Monitoring
|
|
182
|
+
- 🔧 **`usesLaravelHorizon()`** - Validates Laravel Horizon Redis queue manager *(partial: adds ci-deploy-post script if package installed)*
|
|
183
|
+
- **`usesLaravelPennant()`** - Warns if Laravel Pennant feature flags are missing (optional)
|
|
184
|
+
- 🔧 **`usesLaravelPulse()`** - Validates Laravel Pulse application monitoring *(partial: adds PULSE_ENABLED=false to phpunit.xml if package installed)*
|
|
185
|
+
- 🔧 **`cacheAllowsPulseSerializableClasses()`** - On Laravel 13+ with Pulse installed, validates the top-level `serializable_classes` allow-list in config/cache.php permits the classes Pulse round-trips through the cache (`stdClass`, `Illuminate\Support\Collection`, `Carbon\CarbonImmutable`) — the Laravel 13 skeleton ships `'serializable_classes' => false`, which makes every Pulse card fail in production with "tried to access a property on an incomplete object" *(adds the missing classes when the value is `false` or an incomplete array)*
|
|
186
|
+
- 🔧 **`usesLaravelTelescope()`** - Validates Laravel Telescope request debugging *(partial: adds post-update script and TELESCOPE_ENABLED=false to phpunit.xml if package installed)*
|
|
187
|
+
- **`usesSpatieHealthSetup()`** - Validates Spatie Health packages, schedules, s3_health disk, and result store configuration
|
|
188
|
+
- 🔧 **`usesSpatieHealthHasCoreChecks()`** - Validates core health checks (CacheCheck, CpuLoadCheck, DatabaseCheck, DatabaseConnectionCountCheck, DebugModeCheck, EnvironmentCheck, HorizonCheck, QueueCheck, RedisCheck, ScheduleCheck, UsedDiskSpaceCheck) are registered *(adds missing checks to Health::checks() in AppServiceProvider)*
|
|
189
|
+
- 🔧 **`usesSpatieHealthHasLaravelVersionCheck()`** - Validates LaravelVersionCheck is registered in Health::checks() *(adds to AppServiceProvider)*
|
|
190
|
+
- 🔧 **`usesSpatieHealthHasPhpVersionCheck()`** - Validates PhpVersionCheck is registered in Health::checks() *(adds to AppServiceProvider)*
|
|
191
|
+
- 🔧 **`usesSpatieHealthHasReleaseAgeCheck()`** - Validates ReleaseAgeCheck is registered in Health::checks() *(adds to AppServiceProvider)*
|
|
192
|
+
- **`usesSpatieHealthCacheCheckCacheStore()`** - Validates CacheCheck uses the dedicated 'health-checks' cache store via `->driver('health-checks')` in AppServiceProvider and config/cache.php
|
|
193
|
+
- **`usesSpatieHealthQueueCheckCacheStore()`** - Validates QueueCheck: DispatchQueueCheckJobsCommand is scheduled everyMinute(), uses the dedicated 'health-checks' file cache store in AppServiceProvider and config/cache.php
|
|
194
|
+
- **`usesSpatieHealthQueueCheckHorizonQueues()`** - Validates QueueCheck registers all queues from config/horizon.php via onQueue() (requires laravel/horizon)
|
|
195
|
+
- **`usesSpatieHealthScheduleCheckCacheStore()`** - Validates ScheduleCheck uses the dedicated 'health-checks' cache store in both AppServiceProvider and config/cache.php
|
|
196
|
+
- 🔧 **`usesSpatieHealthScheduleCheckHeartbeat()`** - Validates ScheduleCheck is configured with `heartbeatMaxAgeInMinutes(2)` to prevent false positives *(appends the call to ScheduleCheck in AppServiceProvider)*
|
|
197
|
+
- **`usesSpatieBackup()`** - Validates Spatie Backup database backups with comprehensive config validation
|
|
198
|
+
|
|
199
|
+
### Infrastructure & Dependencies
|
|
200
|
+
- **`usesPredis()`** - Validates Predis Redis client is installed
|
|
201
|
+
- **`isLaravelVersionMaintained()`** - Validates Laravel 11+ is used
|
|
202
|
+
- 🔧 **`doesNotUseSail()`** - Validates Sail is NOT used *(partial: deletes docker-compose.yml; run `composer remove laravel/sail` manually)*
|
|
203
|
+
- 🔧 **`doesNotUsePhpInsights()`** - Validates PHP Insights is NOT used *(removes the `nunomaduro/phpinsights` composer.json entry, leftover ci-lint script entries, and config/insights.php; run `composer update` afterward to sync composer.lock)*
|
|
204
|
+
- **`doesNotUseSpatiePasskeysWithFortify()`** - Fails if both `spatie/laravel-passkeys` and `laravel/fortify` are installed, as they overlap in authentication responsibility
|
|
205
|
+
- **`doesNotUseBothBaselineRunners()`** - Fails when `package.json` also declares `@limenet-ch/baseline`: the npm runner is the fallback for projects this package cannot reach, and in a Laravel project this one wins (reports the `npm uninstall` to run; never uninstalls for you)
|
|
206
|
+
- **`doesNotUseHorizonWatcher()`** - Validates Spatie Horizon Watcher is NOT installed
|
|
207
|
+
- 🔧 **`doesNotUseGreaterThanOrEqualConstraints()`** - Validates no `>=` version constraints in composer.json (use `^` or `~` instead) *(replaces `>=X.Y` with `^X.Y` in composer.json)*
|
|
208
|
+
|
|
209
|
+
### CI/CD & Deployment
|
|
210
|
+
- **`hasCiJobs()`** - Validates GitLab CI pipeline jobs are properly configured (the `test` job may extend either `.test` or `.test_db`)
|
|
211
|
+
- 🔧 **`hasTrivyConfig()`** - Validates Trivy security scanning CI job, `trivy.yaml` (scanners, skip-files, skip-dirs, ignorefile, cache.dir, telemetry/VEX/dependency-tree flags, and `pkg.include-dev-deps` so development dependencies are reported instead of silently skipped), presence of `.trivyignore.yaml`, and `.trivycache/` in `.gitignore` *(creates/merges trivy.yaml, creates an empty .trivyignore.yaml, appends to .gitignore, and adds CI job)*
|
|
212
|
+
- **`callsSentryHook()`** - Warns if Sentry error tracking is missing (optional)
|
|
213
|
+
- **`phpVersionMatchesCi()`** - Validates PHP version consistency with CI configuration
|
|
214
|
+
- **`isCiLintComplete()`** - Validates complete linting pipeline
|
|
215
|
+
- 🔧 **`ciSetsNodeVersion()`** - Validates `.gitlab-ci.yml` sets `variables.NODE_VERSION` to `latest`, so the shared CI template resolves the Node version instead of drifting from the project *(adds the variable, creating the `variables` mapping if needed, preserving comments and existing entries)*
|
|
216
|
+
- **`doesNotUseIgnition()`** - Validates Ignition debugger is NOT installed
|
|
217
|
+
|
|
218
|
+
### Local Development
|
|
219
|
+
- **`phpVersionMatchesDdev()`** - Validates PHP version consistency with DDEV
|
|
220
|
+
- 🔧 **`nodeVersion()`** - Validates the project pins Node >= 24 (the current LTS) in both `package.json` `engines.node` and `.nvmrc`, compatible with each other *(creates the missing constraint — establishing Node 24 when none is declared — and bumps a declaration that allows anything older to 24; a newer line such as Node 26 is left alone, and a conflict between existing `engines.node` and `.nvmrc` is reported, not auto-resolved)*
|
|
221
|
+
- 🔧 **`hardensNpmSupplyChain()`** - Hardens npm against supply-chain attacks: requires `package.json` `engines.npm` >= 12 (npm 12 blocks dependency lifecycle scripts by default and refuses git/remote deps), `.npmrc` `engine-strict=true` so that requirement is enforced rather than advisory, and `.npmrc` `min-release-age=7` for a 7-day install cooldown that skips freshly-published (potentially compromised) versions *(sets `engines.npm` to `^12`, and upserts both `.npmrc` keys while preserving existing lines)*
|
|
222
|
+
- 🔧 **`ddevHasPcovPackage()`** - Validates DDEV coverage configuration *(adds pcov to webimage_extra_packages and creates .ddev/php/90-custom.ini)*
|
|
223
|
+
- **`ddevHasRedisAddon()`** - Validates DDEV Redis addon is installed and at minimum version 2.2.0
|
|
224
|
+
- 🔧 **`ddevMutagenIgnoresNodeModules()`** - Validates DDEV Mutagen sync configuration *(creates mutagen.yml and fixes .gitignore)*
|
|
225
|
+
- 🔧 **`ddevNodeVersionIsAuto()`** - Validates `.ddev/config.yaml` sets `nodejs_version: auto` so DDEV derives the Node version from the project's `.nvmrc` instead of pinning its own *(sets `nodejs_version: auto`, preserving surrounding comments and formatting)*
|
|
226
|
+
- **`updatesDdevAddons()`** - Fails if any installed DDEV add-on (`.ddev/addon-metadata/*/manifest.yaml`) has an `install_date` older than 3 months; comment shows the `ddev add-on get <repository>` command to refresh each stale add-on
|
|
227
|
+
|
|
228
|
+
### Build & Release
|
|
229
|
+
- 🔧 **`bumpsComposer()`** - Validates automatic composer dependency bumping *(adds `composer bump` to post-update-cmd)*
|
|
230
|
+
- 🔧 **`usesReleaseIt()`** - Validates automated release management *(partial: creates/fixes .release-it.json and adds release npm script if packages installed)*
|
|
231
|
+
- **`hasNpmScripts()`** - Validates required npm build scripts
|
|
232
|
+
- 🔧 **`biomeUsesLocalSchema()`** - Validates that `biome.json`, when the project has one, points `$schema` at `./node_modules/@biomejs/biome/configuration_schema.json` rather than a version-pinned remote URL, so the schema follows the installed Biome instead of needing a manual bump on every update. Passes when the project does not use Biome. *(rewrites or inserts the `$schema` line as a targeted text edit, leaving the rest of the file — comments included — byte-identical, since Biome formats `biome.json` itself)*
|
|
233
|
+
|
|
234
|
+
### Security & Configuration
|
|
235
|
+
- 🔧 **`modelShouldBeStrict()`** - Validates `Model::shouldBeStrict()` is called in AppServiceProvider with `true`, no argument, or a dynamic expression (not `false`) *(adds `Model::shouldBeStrict(! app()->isProduction())` to boot())*
|
|
236
|
+
- 🔧 **`formRequestFailOnUnknownFields()`** - Validates `FormRequest::failOnUnknownFields()` is called in AppServiceProvider (Laravel ≥13.6 only; warns on older versions) *(adds `FormRequest::failOnUnknownFields(! app()->isProduction())` to boot())*
|
|
237
|
+
- **`hasDailyLogging()`** - Validates logging uses `daily` channel (directly or via `stack`)
|
|
238
|
+
- **`hasEncryptedEnvFile()`** - Validates encrypted environment file exists
|
|
239
|
+
- **`usesReadableEncryptedEnvFile()`** - Validates the encrypted env file uses the readable line-per-variable format produced by `ddev artisan env:encrypt --readable` (variable names stay visible in diffs), not the opaque blob format. Passes when no encrypted file exists (existence is `hasEncryptedEnvFile`'s concern).
|
|
240
|
+
- **`doesNotPinOldMailTemplate()`** - Fails if a published mail view that pins the old template (`resources/views/vendor/mail/html/themes/default.css` or `html/header.blade.php`) exists, preventing adoption of Laravel's modernized mail template.
|
|
241
|
+
- 🔧 **`callsBaseline()`** - Validates self-validation runs after updates *(adds/upgrades post-update-cmd entry to include `--fix`)*
|
|
242
|
+
- **`doesNotCallPeriodicBaselineOnUpdate()`** - Fails if `php artisan limenet:laravel-baseline:periodic` is in the `post-update-cmd` scripts (it shouldn't be — periodic checks fail CI automatically when expired)
|
|
243
|
+
- 🔧 **`doesNotHaveGuidelinesScript()`** - Fails if the removed `php artisan limenet:laravel-baseline:guidelines` command is still in `post-update-cmd` (removed in v2.1.0) *(removes the entry from composer.json)*
|
|
244
|
+
- 🔧 **`isInstalledAsRegularDependency()`** - Validates `limenet/laravel-baseline` is in `require` (not `require-dev`) *(moves from require-dev to require in composer.json)*
|
|
245
|
+
- 🔧 **`usesLaravelLang()`** - Validates `laravel-lang/lang` dev dependency is installed with `lang:update` and pint in post-update scripts *(partial: adds post-update scripts if package in require-dev)*
|
|
246
|
+
|
|
247
|
+
## Testing
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
composer test
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Changelog
|
|
254
|
+
|
|
255
|
+
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
|
|
256
|
+
|
|
257
|
+
## Contributing
|
|
258
|
+
|
|
259
|
+
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
|
260
|
+
|
|
261
|
+
## Security Vulnerabilities
|
|
262
|
+
|
|
263
|
+
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
|
|
264
|
+
|
|
265
|
+
## Credits
|
|
266
|
+
|
|
267
|
+
- [Linus Metzler](https://github.com/limenet)
|
|
268
|
+
- [All Contributors](../../contributors)
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
package/js/dist/bin.d.ts
ADDED
package/js/dist/bin.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { runCheck } from './commands/check.js';
|
|
4
|
+
import { runInstallSkills } from './commands/install-skills.js';
|
|
5
|
+
import { runPeriodic } from './commands/periodic.js';
|
|
6
|
+
import { Project } from './project.js';
|
|
7
|
+
const USAGE = `baseline — a highly opinionated baseline for JS/TS projects.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
baseline check [--fix] [--verbose] [--cwd <path>]
|
|
11
|
+
baseline periodic [--cwd <path>]
|
|
12
|
+
baseline install-skills [--force] [--cwd <path>]
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--fix Repair what can be repaired, then re-verify
|
|
16
|
+
--verbose Show passing checks too
|
|
17
|
+
--force Overwrite skills that are already installed
|
|
18
|
+
--cwd Project root to inspect (default: the current directory)
|
|
19
|
+
--help Show this message
|
|
20
|
+
`;
|
|
21
|
+
async function main() {
|
|
22
|
+
const { values, positionals } = parseArgs({
|
|
23
|
+
allowPositionals: true,
|
|
24
|
+
options: {
|
|
25
|
+
fix: { type: 'boolean', default: false },
|
|
26
|
+
verbose: { type: 'boolean', short: 'v', default: false },
|
|
27
|
+
force: { type: 'boolean', default: false },
|
|
28
|
+
cwd: { type: 'string' },
|
|
29
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const command = positionals[0];
|
|
33
|
+
if (values.help || command === undefined) {
|
|
34
|
+
console.log(USAGE);
|
|
35
|
+
return values.help ? 0 : 1;
|
|
36
|
+
}
|
|
37
|
+
const project = Project.at(values.cwd);
|
|
38
|
+
switch (command) {
|
|
39
|
+
case 'check':
|
|
40
|
+
return runCheck(project, { fix: values.fix, verbose: values.verbose });
|
|
41
|
+
case 'periodic':
|
|
42
|
+
return await runPeriodic(project);
|
|
43
|
+
case 'install-skills':
|
|
44
|
+
return runInstallSkills(project, { force: values.force });
|
|
45
|
+
default:
|
|
46
|
+
console.error(`Unknown command "${command}".\n\n${USAGE}`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
process.exitCode = await main();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Unreadable policy, malformed JSON in the project: actionable on its own, so
|
|
55
|
+
// it is reported as a message rather than as a stack trace.
|
|
56
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,KAAK,GAAG;;;;;;;;;;;;;CAab,CAAA;AAED,KAAK,UAAU,IAAI;IACf,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACtC,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACL,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACxC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;YACxD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YAC1C,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;SACxD;KACJ,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAE9B,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAElB,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAEtC,QAAQ,OAAO,EAAE,CAAC;QACd,KAAK,OAAO;YACR,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;QAC1E,KAAK,UAAU;YACX,OAAO,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;QACrC,KAAK,gBAAgB;YACjB,OAAO,gBAAgB,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QAC7D;YACI,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,SAAS,KAAK,EAAE,CAAC,CAAA;YAE1D,OAAO,CAAC,CAAA;IAChB,CAAC;AACL,CAAC;AAED,IAAI,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAA;AACnC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACb,8EAA8E;IAC9E,4DAA4D;IAC5D,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAEjF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CheckResult } from './check.js';
|
|
2
|
+
import { ClaudeSettingsCheck } from './claude-settings-check.js';
|
|
3
|
+
/**
|
|
4
|
+
* Only the shared half of the policy's allow list applies here — the DDEV and
|
|
5
|
+
* artisan entries the PHP runner also requires are unsatisfiable in a project
|
|
6
|
+
* with neither.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AllowsToolingInClaudeSettingsCheck extends ClaudeSettingsCheck {
|
|
9
|
+
static readonly checkName = "allowsToolingInClaudeSettings";
|
|
10
|
+
fix(dry?: boolean): CheckResult;
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { ClaudeSettingsCheck } from './claude-settings-check.js';
|
|
3
|
+
/**
|
|
4
|
+
* Only the shared half of the policy's allow list applies here — the DDEV and
|
|
5
|
+
* artisan entries the PHP runner also requires are unsatisfiable in a project
|
|
6
|
+
* with neither.
|
|
7
|
+
*/
|
|
8
|
+
export class AllowsToolingInClaudeSettingsCheck extends ClaudeSettingsCheck {
|
|
9
|
+
static checkName = 'allowsToolingInClaudeSettings';
|
|
10
|
+
fix(dry = false) {
|
|
11
|
+
return this.requireEntries('allow', policy().strings('claude.allow.shared'), (entry) => `Claude settings: add "${entry}" to permissions.allow in .claude/settings.json`, dry);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=allows-tooling-in-claude-settings-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allows-tooling-in-claude-settings-check.js","sourceRoot":"","sources":["../../src/checks/allows-tooling-in-claude-settings-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;GAIG;AACH,MAAM,OAAO,kCAAmC,SAAQ,mBAAmB;IACvE,MAAM,CAAmB,SAAS,GAAG,+BAA+B,CAAA;IAEpE,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,OAAO,IAAI,CAAC,cAAc,CACtB,OAAO,EACP,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,EACvC,CAAC,KAAK,EAAE,EAAE,CAAC,yBAAyB,KAAK,iDAAiD,EAC1F,GAAG,CACN,CAAA;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* Biome's `$schema` must resolve out of node_modules rather than name a released
|
|
4
|
+
* version. The local path always describes the Biome that is actually installed,
|
|
5
|
+
* so upgrading Biome does not also mean editing biome.json — a versioned remote
|
|
6
|
+
* URL leaves the editor validating against whichever version was current when
|
|
7
|
+
* someone last remembered to bump the line.
|
|
8
|
+
*
|
|
9
|
+
* A project without Biome is none of this check's business: no config file means
|
|
10
|
+
* pass, not a warning.
|
|
11
|
+
*
|
|
12
|
+
* Both the detection and the fix work on the raw text rather than a decoded
|
|
13
|
+
* document, for two reasons: Biome reads its config as JSONC, so a file with
|
|
14
|
+
* comments is valid and must not be reported as broken; and biome.json is
|
|
15
|
+
* formatted by Biome itself, so re-encoding the whole file would leave
|
|
16
|
+
* `biome ci` failing on the very file this fix just touched.
|
|
17
|
+
*/
|
|
18
|
+
export declare class BiomeUsesLocalSchemaCheck extends FixableCheck {
|
|
19
|
+
static readonly checkName = "biomeUsesLocalSchema";
|
|
20
|
+
fix(dry?: boolean): CheckResult;
|
|
21
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { FixableCheck } from './check.js';
|
|
3
|
+
/** The `$schema` string literal, captured with its JSON escapes intact. */
|
|
4
|
+
const SCHEMA_PATTERN = /"\$schema"\s*:\s*"((?:[^"\\]|\\.)*)"/;
|
|
5
|
+
/**
|
|
6
|
+
* Biome's `$schema` must resolve out of node_modules rather than name a released
|
|
7
|
+
* version. The local path always describes the Biome that is actually installed,
|
|
8
|
+
* so upgrading Biome does not also mean editing biome.json — a versioned remote
|
|
9
|
+
* URL leaves the editor validating against whichever version was current when
|
|
10
|
+
* someone last remembered to bump the line.
|
|
11
|
+
*
|
|
12
|
+
* A project without Biome is none of this check's business: no config file means
|
|
13
|
+
* pass, not a warning.
|
|
14
|
+
*
|
|
15
|
+
* Both the detection and the fix work on the raw text rather than a decoded
|
|
16
|
+
* document, for two reasons: Biome reads its config as JSONC, so a file with
|
|
17
|
+
* comments is valid and must not be reported as broken; and biome.json is
|
|
18
|
+
* formatted by Biome itself, so re-encoding the whole file would leave
|
|
19
|
+
* `biome ci` failing on the very file this fix just touched.
|
|
20
|
+
*/
|
|
21
|
+
export class BiomeUsesLocalSchemaCheck extends FixableCheck {
|
|
22
|
+
static checkName = 'biomeUsesLocalSchema';
|
|
23
|
+
fix(dry = false) {
|
|
24
|
+
const configFile = policy().string('biome.configFile');
|
|
25
|
+
const contents = this.project.read(configFile);
|
|
26
|
+
if (contents === null) {
|
|
27
|
+
return 'pass';
|
|
28
|
+
}
|
|
29
|
+
if (!contents.includes('{')) {
|
|
30
|
+
this.comment(`${configFile} is empty or unreadable`);
|
|
31
|
+
return 'fail';
|
|
32
|
+
}
|
|
33
|
+
const expected = policy().string('biome.schema');
|
|
34
|
+
const declared = declaredSchema(contents);
|
|
35
|
+
if (declared === expected) {
|
|
36
|
+
return 'pass';
|
|
37
|
+
}
|
|
38
|
+
this.comment(declared === null
|
|
39
|
+
? `Missing "$schema" in ${configFile}: set it to "${expected}" so the schema follows the installed Biome`
|
|
40
|
+
: `Invalid "$schema" in ${configFile}: must be "${expected}" (found "${declared}"), so it does not need bumping on every Biome update`);
|
|
41
|
+
if (dry) {
|
|
42
|
+
return 'fail';
|
|
43
|
+
}
|
|
44
|
+
this.project.write(configFile, declared === null ? insertSchema(contents, expected) : replaceSchema(contents, expected));
|
|
45
|
+
return this.fix(true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** The decoded `$schema` value, or null if the file declares none. */
|
|
49
|
+
function declaredSchema(contents) {
|
|
50
|
+
const literal = SCHEMA_PATTERN.exec(contents)?.[1];
|
|
51
|
+
if (literal === undefined) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const decoded = JSON.parse(`"${literal}"`);
|
|
56
|
+
return typeof decoded === 'string' ? decoded : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function replaceSchema(contents, expected) {
|
|
63
|
+
// A function, not a replacement string: the literal "$schema" would otherwise
|
|
64
|
+
// have to be escaped against `$`-pattern interpolation.
|
|
65
|
+
return contents.replace(SCHEMA_PATTERN, () => `"$schema": ${JSON.stringify(expected)}`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Prepends the key inside the top-level object, indented like whatever key
|
|
69
|
+
* currently comes first — Biome's own convention is `$schema` first.
|
|
70
|
+
*/
|
|
71
|
+
function insertSchema(contents, expected) {
|
|
72
|
+
const open = contents.indexOf('{');
|
|
73
|
+
const rest = contents.slice(open + 1);
|
|
74
|
+
const indent = /\n([ \t]+)\S/.exec(rest)?.[1] ?? ' ';
|
|
75
|
+
const separator = rest.trim() === '}' ? '' : ',';
|
|
76
|
+
return `${contents.slice(0, open + 1)}\n${indent}"$schema": ${JSON.stringify(expected)}${separator}${rest}`;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=biome-uses-local-schema-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"biome-uses-local-schema-check.js","sourceRoot":"","sources":["../../src/checks/biome-uses-local-schema-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* The runner must actually run. The PHP counterpart hooks composer's
|
|
4
|
+
* post-update-cmd; npm has no equivalent lifecycle hook that survives npm 12's
|
|
5
|
+
* script blocking, so the JS project runs it from `ci-lint` instead — the script
|
|
6
|
+
* the CI template and the Claude Stop hook both already invoke.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CallsBaselineCheck extends FixableCheck {
|
|
9
|
+
static readonly checkName = "callsBaseline";
|
|
10
|
+
fix(dry?: boolean): CheckResult;
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { FixableCheck } from './check.js';
|
|
3
|
+
/**
|
|
4
|
+
* The runner must actually run. The PHP counterpart hooks composer's
|
|
5
|
+
* post-update-cmd; npm has no equivalent lifecycle hook that survives npm 12's
|
|
6
|
+
* script blocking, so the JS project runs it from `ci-lint` instead — the script
|
|
7
|
+
* the CI template and the Claude Stop hook both already invoke.
|
|
8
|
+
*/
|
|
9
|
+
export class CallsBaselineCheck extends FixableCheck {
|
|
10
|
+
static checkName = 'callsBaseline';
|
|
11
|
+
fix(dry = false) {
|
|
12
|
+
const command = policy().string('ciLint.baselineCommand.js');
|
|
13
|
+
const packageJson = this.project.readJson('package.json');
|
|
14
|
+
if (packageJson === null) {
|
|
15
|
+
this.comment('Package.json missing: Create package.json in project root');
|
|
16
|
+
return 'fail';
|
|
17
|
+
}
|
|
18
|
+
const script = packageJson.scripts?.['ci-lint'];
|
|
19
|
+
if (script?.includes(command)) {
|
|
20
|
+
return 'pass';
|
|
21
|
+
}
|
|
22
|
+
this.comment(`ci-lint must run the baseline: add "${command}" to the ci-lint script in package.json`);
|
|
23
|
+
if (dry) {
|
|
24
|
+
return 'fail';
|
|
25
|
+
}
|
|
26
|
+
packageJson.scripts = {
|
|
27
|
+
...packageJson.scripts,
|
|
28
|
+
'ci-lint': script === undefined || script.trim() === '' ? command : `${script} && ${command}`,
|
|
29
|
+
};
|
|
30
|
+
this.project.writeJson('package.json', packageJson);
|
|
31
|
+
return this.fix(true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=calls-baseline-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calls-baseline-check.js","sourceRoot":"","sources":["../../src/checks/calls-baseline-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAM3D;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IAChD,MAAM,CAAmB,SAAS,GAAG,eAAe,CAAA;IAEpD,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAA;QAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAc,cAAc,CAAC,CAAA;QAEtE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAA;YAEzE,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;QAE/C,IAAI,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,uCAAuC,OAAO,yCAAyC,CAAC,CAAA;QAErG,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,WAAW,CAAC,OAAO,GAAG;YAClB,GAAG,WAAW,CAAC,OAAO;YACtB,SAAS,EAAE,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,OAAO,OAAO,EAAE;SAChG,CAAA;QAED,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAEnD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;CACJ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Project } from '../project.js';
|
|
2
|
+
/** Mirrors Limenet\LaravelBaseline\Enums\CheckResult, values included. */
|
|
3
|
+
export type CheckResult = 'pass' | 'fail' | 'warn';
|
|
4
|
+
export declare function isError(result: CheckResult): boolean;
|
|
5
|
+
export declare function icon(result: CheckResult): string;
|
|
6
|
+
/** Collects the actionable messages a single check run produced. */
|
|
7
|
+
export declare class CommentCollector {
|
|
8
|
+
private comments;
|
|
9
|
+
add(comment: string): void;
|
|
10
|
+
all(): string[];
|
|
11
|
+
reset(): void;
|
|
12
|
+
}
|
|
13
|
+
export declare abstract class Check {
|
|
14
|
+
protected readonly project: Project;
|
|
15
|
+
protected readonly comments: CommentCollector;
|
|
16
|
+
/**
|
|
17
|
+
* The check's stable identifier — the same string the PHP runner derives from
|
|
18
|
+
* its class name, and the key used in .baseline.json excludes and in fixtures.
|
|
19
|
+
* Declared explicitly rather than derived, so a bundler renaming a class can
|
|
20
|
+
* never silently rename a check.
|
|
21
|
+
*/
|
|
22
|
+
static readonly checkName: string;
|
|
23
|
+
constructor(project: Project, comments: CommentCollector);
|
|
24
|
+
abstract check(): CheckResult;
|
|
25
|
+
protected comment(message: string): void;
|
|
26
|
+
get name(): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A check that can repair what it detects. check() is the dry run, so detection
|
|
30
|
+
* and repair can never drift apart — the same shape as the PHP
|
|
31
|
+
* AbstractFixableCheck.
|
|
32
|
+
*/
|
|
33
|
+
export declare abstract class FixableCheck extends Check {
|
|
34
|
+
check(): CheckResult;
|
|
35
|
+
abstract fix(dry?: boolean): CheckResult;
|
|
36
|
+
}
|
|
37
|
+
export declare function isFixable(check: Check): check is FixableCheck;
|