diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 0000000..83b8054
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,44 @@
+{
+ "hooks": {
+ "PreToolUse": [
+ {
+ "matcher": "Edit|Write",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "case \"$CLAUDE_FILE_PATH\" in */vendor/*|*/composer.lock) echo 'BLOCK: Do not edit vendor or lock files directly' >&2; exit 1;; esac"
+ }
+ ]
+ }
+ ],
+ "PostToolUse": [
+ {
+ "matcher": "Edit|Write",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "case \"$CLAUDE_FILE_PATH\" in *.php) vendor/bin/phpcbf --standard=phpcs.xml \"$CLAUDE_FILE_PATH\" 2>/dev/null; true;; esac",
+ "timeout": 10000
+ }
+ ]
+ }
+ ]
+ },
+ "permissions": {
+ "allow": [
+ "Bash(composer test:*)",
+ "Bash(composer lint:*)",
+ "Bash(composer lint:all:*)",
+ "Bash(composer format:*)",
+ "Bash(composer format:md:*)",
+ "Bash(composer coverage:*)",
+ "Bash(composer build:*)",
+ "Bash(vendor/bin/pest:*)",
+ "Bash(vendor/bin/phpcs:*)",
+ "Bash(vendor/bin/phpcbf:*)",
+ "Bash(git log:*)",
+ "Bash(git diff:*)",
+ "Bash(git status:*)"
+ ]
+ }
+}
diff --git a/.claude/skills/build-phar/SKILL.md b/.claude/skills/build-phar/SKILL.md
new file mode 100644
index 0000000..6f4baee
--- /dev/null
+++ b/.claude/skills/build-phar/SKILL.md
@@ -0,0 +1,16 @@
+---
+name: build-phar
+description: Build PHAR executable and run smoke test
+disable-model-invocation: true
+---
+
+# Build PHAR
+
+Build the PHAR executable and verify it works.
+
+## Steps
+
+1. Run `composer test` — abort if any tests fail
+2. Run `composer build` — build PHAR to `builds/branch-usage-checker`
+3. Run `builds/branch-usage-checker --version` — verify it launches successfully
+4. Report the file size of `builds/branch-usage-checker` and confirm success
diff --git a/.claude/skills/release-check/SKILL.md b/.claude/skills/release-check/SKILL.md
new file mode 100644
index 0000000..04303d0
--- /dev/null
+++ b/.claude/skills/release-check/SKILL.md
@@ -0,0 +1,18 @@
+---
+name: release-check
+description: Run full pre-release verification suite
+disable-model-invocation: true
+---
+
+# Pre-Release Verification
+
+Run the complete verification suite before a release.
+
+## Steps
+
+1. Run `composer lint:all` — all linters must pass (PHPCS, EditorConfig, markdownlint)
+2. Run `composer test` — all tests must pass
+3. Run `composer build` — PHAR must build successfully
+4. Run `builds/branch-usage-checker --version` — smoke test the PHAR
+5. Run `git status` — working tree should be clean (no uncommitted changes)
+6. Report go/no-go summary with results of each step
diff --git a/.editorconfig b/.editorconfig
index 40006b5..957db0f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,12 +9,14 @@ indent_size = 4
trim_trailing_whitespace = true
[*.md]
+indent_size = 2
trim_trailing_whitespace = false
-[*.json]
+[*.xml]
indent_size = 2
-insert_final_newline = true
-[*.yml]
-indent_style = space
+[*.{json,jsonc}]
+indent_size = 2
+
+[*.{yml,yaml}]
indent_size = 2
diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json
new file mode 100644
index 0000000..1b8d0a4
--- /dev/null
+++ b/.editorconfig-checker.json
@@ -0,0 +1,7 @@
+{
+ "Exclude": [
+ "\\.md$",
+ "builds/",
+ "vendor/"
+ ]
+}
diff --git a/.gitignore b/.gitignore
index b9292c5..fe68da1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,18 @@
+# OS files
+.DS_Store
+Thumbs.db
+
/vendor
/.idea
/.vscode
/.vagrant
+
+# PHPUnit
.phpunit.result.cache
+.phpunit.cache
+
+# Build output
/builds/branch-usage-checker
+
+# Claude Code local settings
+.claude/settings.local.json
diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc
new file mode 100644
index 0000000..9816dd5
--- /dev/null
+++ b/.markdownlint-cli2.jsonc
@@ -0,0 +1,3 @@
+{
+ "ignores": ["vendor/**", "node_modules/**"]
+}
diff --git a/.markdownlint.json b/.markdownlint.json
new file mode 100644
index 0000000..afd337c
--- /dev/null
+++ b/.markdownlint.json
@@ -0,0 +1,5 @@
+{
+ "line-length": { "line_length": 80 },
+ "no-duplicate-heading": false,
+ "required-headings": false
+}
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..55b4f02
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,81 @@
+# Branch Usage Checker
+
+This file provides guidance to Claude Code
+(claude.ai/code) when working with code in this
+repository.
+
+## Project Overview
+
+Branch Usage Checker is a Laravel Zero CLI tool
+that cross-references GitHub branches with
+Packagist download statistics to identify branches
+safe to delete. Built as a PHAR-distributable PHP
+application.
+
+## Commands
+
+- `composer install` — Install dependencies
+- `composer test` — Run tests (Pest v4)
+- `composer build` — Build PHAR executable
+ to `builds/branch-usage-checker`
+- `composer lint` — Check code style (PHPCS)
+- `composer lint:phpmd` — Static analysis (PHPMD)
+- `composer lint:md` — Lint markdown files
+- `composer lint:ec` — Check EditorConfig compliance
+- `composer lint:all` — Run all linters
+- `composer format` — Auto-fix code style (PHPCBF)
+- `composer format:md` — Format Markdown tables
+- `composer x` — Run the built PHAR
+- `vendor/bin/pest --filter "test name"` — Run a
+ single test
+
+## Code Standards
+
+- PSR-12 via PHP CodeSniffer (`phpcs.xml`),
+ with `PSR12.Operators.OperatorSpacing` excluded
+- PHP 8.4 required
+- Composer normalize runs automatically on
+ autoload dump
+- CaptainHook pre-commit hook runs PHPCBF
+ then PHPCS on staged PHP files automatically
+
+## Architecture
+
+This is a Laravel Zero console application.
+Entry point is `./application`, which bootstraps
+via `bootstrap/app.php`.
+
+### Core Flow (CheckCommand)
+
+`check {vendor} {package?} {months=9}` — the main
+(and only functional) command. The `vendor` argument
+accepts a combined `vendor/package` form, making
+`package` optional in that case:
+
+1. Fetches package metadata from
+ `packagist.org/packages/{vendor}/{package}.json`
+2. Extracts branches (versions prefixed with
+ `dev-`)
+3. For each branch, fetches monthly download
+ stats from Packagist over the configured
+ lookback window
+4. Displays a statistics table and a suggestions
+ table (branches with zero downloads)
+
+### Key Directories
+
+- `app/Commands/` — CLI commands
+ (CheckCommand is the primary one)
+- `app/Dto/` — Spatie DataTransferObject classes
+ for Packagist API responses
+- `tests/Feature/Commands/` — Feature tests
+ for commands
+- `builds/` — PHAR output directory
+
+### Dependencies of Note
+
+- HTTP requests use `Illuminate\Http\Client\Factory`
+ (Guzzle-backed), injected via the container
+- DTOs use `spatie/data-transfer-object` with
+ `MapFrom` attributes for JSON field mapping
+- PHAR building configured in `box.json`
diff --git a/app/Commands/CheckCommand.php b/app/Commands/CheckCommand.php
index a871a15..e18cb69 100644
--- a/app/Commands/CheckCommand.php
+++ b/app/Commands/CheckCommand.php
@@ -3,14 +3,15 @@
namespace App\Commands;
use App\Dto\PackagistApiPackagePayload;
-use Illuminate\Support\Facades\Http;
+use Illuminate\Http\Client\Factory as HttpFactory;
+use Illuminate\Http\Client\Pool;
use LaravelZero\Framework\Commands\Command;
class CheckCommand extends Command
{
protected $signature = 'check
- {vendor : Package vendor (required)}
- {package : Package name (required)}
+ {vendor : Package vendor or vendor/package}
+ {package? : Package name}
{months=9 : How many months should we return for review (optional)}
';
protected $description = 'Check package branch usage';
@@ -20,23 +21,30 @@ class CheckCommand extends Command
private string $filter = '';
private int $totalBranches = 0;
+ private const NAME_PATTERN = '/^[a-z0-9]([_.\-]?[a-z0-9]+)*$/';
+ private const TIMEOUT_SECONDS = 10;
+ private const PACKAGIST_URL = 'https://packagist.org/packages/%s/%s';
+
+ private HttpFactory $http;
+
+ /** Execute the check command. */
public function handle(): int
{
- $this->vendor = (string)$this->argument('vendor');
- $this->package = (string)$this->argument('package');
- $months = (int)$this->argument('months');
+ $this->http = resolve(HttpFactory::class);
- $this->info('Checking: ' . sprintf('%s/%s', $this->vendor, $this->package));
+ if (!$this->resolveInput()) {
+ return 1;
+ }
+
+ $months = (int) $this->argument('months');
+
+ $this->info(sprintf('Checking: %s/%s', $this->vendor, $this->package));
$this->info('Months: ' . $months);
- $payload = Http::get(
- sprintf(
- 'https://packagist.org/packages/%s/%s.json',
- $this->vendor,
- $this->package
- )
- );
-
+ $payload = $this->fetchPackageMetadata();
+ if ($payload === null) {
+ return 1;
+ }
$this->filter = now()->subMonths($months)->day(1)->toDateString();
try {
@@ -45,9 +53,9 @@ class CheckCommand extends Command
$versions = collect($pkg->versions ?? [])
->keys()
- // Filter actual versions out.
->filter(fn ($version) => \str_starts_with($version, 'dev-'))
- ->sort();
+ ->sort()
+ ->values();
$this->totalBranches = $versions->count();
@@ -58,48 +66,150 @@ class CheckCommand extends Command
)
);
- $statistics = collect($versions)
- ->mapWithKeys(fn ($branch) => $this->getStatistics($branch))
- ->toArray();
+ $responses = $this->http->pool(
+ fn (Pool $pool) => $versions->map(
+ fn ($branch) => $pool->as($branch)->timeout(self::TIMEOUT_SECONDS)->get($this->getStatsUrl($branch))
+ )->toArray()
+ );
+
+ $statistics = $this->collectBranchStats($versions, $responses);
$this->info('Downloaded statistics...');
- $this->outputTable($statistics);
- $this->outputSuggestions($statistics);
- } catch (\Exception $e) {
- $this->error($e->getMessage(), $e);
+ if ($this->outputTable($statistics)) {
+ $this->outputSuggestions($statistics);
+ }
+ } catch (\Throwable $e) {
+ if ($e instanceof \TypeError) {
+ throw $e;
+ }
+ $this->error($e->getMessage());
+ return 1;
}
return 0;
}
- private function getStatistics(string $branch): array
+ /** Parse and validate vendor/package input arguments. */
+ private function resolveInput(): bool
{
- $payload = Http::get(
+ $vendor = strtolower((string) $this->argument('vendor'));
+ $package = $this->argument('package');
+
+ if (str_contains($vendor, '/')) {
+ if ($package !== null) {
+ $this->error(
+ 'Conflicting arguments: vendor/package format'
+ . ' and separate package argument cannot be used together.'
+ );
+ return false;
+ }
+ [$vendor, $package] = explode('/', $vendor, 2);
+ }
+
+ if ($package === null || $package === '') {
+ $this->error('Missing package name. Usage: check vendor/package or check vendor package');
+ return false;
+ }
+
+ $package = strtolower((string) $package);
+
+ if (!preg_match(self::NAME_PATTERN, $vendor)) {
+ $this->error("Invalid vendor name: {$vendor}");
+ return false;
+ }
+
+ if (!preg_match(self::NAME_PATTERN, $package)) {
+ $this->error("Invalid package name: {$package}");
+ return false;
+ }
+
+ $this->vendor = $vendor;
+ $this->package = $package;
+
+ return true;
+ }
+
+ /** Fetch package metadata from Packagist. */
+ private function fetchPackageMetadata(): ?\Illuminate\Http\Client\Response
+ {
+ $payload = $this->http->timeout(self::TIMEOUT_SECONDS)->get(
sprintf(
- 'https://packagist.org/packages/%s/%s/stats/%s.json?average=monthly&from=%s',
+ self::PACKAGIST_URL . '.json',
$this->vendor,
- $this->package,
- $branch,
- $this->filter
+ $this->package
)
);
- $data = collect($payload->json());
- $labels = collect($data->get('labels', []))->toArray();
- $values = collect($data->get('values', []))->flatten()->toArray();
+ if ($payload->failed()) {
+ if ($payload->status() === 404) {
+ $this->error("Package not found: {$this->vendor}/{$this->package}");
+ return null;
+ }
+ $this->error("Failed to fetch package metadata (HTTP {$payload->status()})");
+ return null;
+ }
- $labels[] = 'Total';
- $values[] = array_sum($values);
-
- return [$branch => \array_combine($labels, $values)];
+ return $payload;
}
- private function outputTable(array $statistics): void
+ /** Build the Packagist stats API URL for a branch. */
+ private function getStatsUrl(string $branch): string
+ {
+ return sprintf(
+ self::PACKAGIST_URL . '/stats/%s.json?average=monthly&from=%s',
+ $this->vendor,
+ $this->package,
+ $branch,
+ $this->filter
+ );
+ }
+
+ /** Parse pooled responses into branch download statistics. */
+ private function collectBranchStats($versions, array $responses): array
+ {
+ $statistics = [];
+ foreach ($versions as $branch) {
+ $response = $responses[$branch];
+
+ if ($response instanceof \Throwable) {
+ $this->warn("Failed to fetch stats for {$branch}, skipping.");
+ continue;
+ }
+
+ if ($response->failed()) {
+ $this->warn("Failed to fetch stats for {$branch} (HTTP {$response->status()}), skipping.");
+ continue;
+ }
+
+ $data = collect($response->json());
+ $labels = collect($data->get('labels', []))->toArray();
+ $values = collect($data->get('values', []))->flatten()->toArray();
+
+ $labels[] = 'Total';
+ $values[] = array_sum($values);
+
+ if (count($labels) !== count($values)) {
+ $this->warn(sprintf(
+ 'Malformed stats for %s (labels: %d, values: %d), skipping.',
+ $branch,
+ count($labels),
+ count($values)
+ ));
+ continue;
+ }
+ $statistics[$branch] = \array_combine($labels, $values);
+ }
+
+ return $statistics;
+ }
+
+ /** Render the download statistics table. */
+ private function outputTable(array $statistics): bool
{
if (empty($statistics)) {
$this->info('No statistics found... Stopping.');
- exit(0);
+ return false;
}
$tableHeaders = ['' => 'Branch'];
@@ -107,44 +217,41 @@ class CheckCommand extends Command
foreach ($statistics as $branch => $stats) {
foreach ($stats as $m => $v) {
- $tableHeaders[$m] = (string)$m;
+ $tableHeaders[$m] = (string) $m;
$tableBranches[$branch][$branch] = $branch;
- $tableBranches[$branch][$m] = (string)$v;
+ $tableBranches[$branch][$m] = (string) $v;
}
}
$this->line('');
$this->table($tableHeaders, $tableBranches);
+
+ return true;
}
- private function outputSuggestions(array $statistics = []): void
+ /** Render suggestions for zero-download branches. */
+ private function outputSuggestions(array $statistics): void
{
$deletable = [];
- if (empty($statistics)) {
- $this->info('No statistics to give suggestions for. Quitting...');
- exit(0);
- }
foreach ($statistics as $k => $values) {
if (!empty($values['Total'])) {
continue;
}
- $deletable[$k] = $values['Total'];
+ $deletable[] = $k;
}
if (empty($deletable)) {
$this->info('No suggestions available. Good job!');
- exit(0);
+ return;
}
- $keys = array_keys($deletable);
-
- $branches = collect($keys)->mapWithKeys(function ($branch) {
+ $branches = collect($deletable)->mapWithKeys(function ($branch) {
return [
$branch => [
$branch,
sprintf(
- 'https://packagist.org/packages/%s/%s#%s',
+ self::PACKAGIST_URL . '#%s',
$this->vendor,
$this->package,
$branch
diff --git a/app/Commands/InspireCommand.php b/app/Commands/InspireCommand.php
deleted file mode 100644
index 58c856a..0000000
--- a/app/Commands/InspireCommand.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
Laravel Zero
- Simplicity is the ultimate sophistication.
-
- HTML
- );
- }
-
- /**
- * Define the command's schedule.
- *
- * @param \Illuminate\Console\Scheduling\Schedule $schedule
- *
- * @return void
- */
- public function schedule(Schedule $schedule)
- {
- // $schedule->command(static::class)->everyMinute();
- }
-}
diff --git a/app/Dto/GitHubApiBranch.php b/app/Dto/GitHubApiBranch.php
deleted file mode 100644
index 9eabb18..0000000
--- a/app/Dto/GitHubApiBranch.php
+++ /dev/null
@@ -1,11 +0,0 @@
-flatten(1)
- ->toArray();
-
- return $pages;
- }
-
- public static function downloader($vendor, $package): array
- {
- $responses = [];
-
- $continue = true;
- $page = 1;
- $gh_api = sprintf(
- 'https://api.github.com/repos/%s/%s/branches?per_page=100',
- $vendor,
- $package
- );
-
- while ($continue) {
- $response = Http::get($gh_api . '&page=' . $page);
-
- if (empty($response)) {
- $continue = false;
- }
-
- $responses[$page] = $response;
- $page++;
- }
-
- return $responses;
- }
-}
diff --git a/builds/branch-usage-checker b/builds/branch-usage-checker
index 2d71eb8..37e4fa5 100755
Binary files a/builds/branch-usage-checker and b/builds/branch-usage-checker differ
diff --git a/captainhook.json b/captainhook.json
new file mode 100644
index 0000000..0dcb521
--- /dev/null
+++ b/captainhook.json
@@ -0,0 +1,51 @@
+{
+ "pre-commit": {
+ "enabled": true,
+ "actions": [
+ {
+ "action": "vendor/bin/phpcbf --standard=phpcs.xml {$STAGED_FILES|of-type:php}",
+ "config": {
+ "label": "Fix code style with PHPCBF"
+ }
+ },
+ {
+ "action": "vendor/bin/phpcs --standard=phpcs.xml {$STAGED_FILES|of-type:php}",
+ "config": {
+ "label": "Check code style with PHPCS"
+ }
+ },
+ {
+ "action": "vendor/bin/phpmd {$STAGED_FILES|of-type:php|separated-by:,} text phpmd.xml",
+ "conditions": [
+ {
+ "exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType",
+ "args": ["php"]
+ }
+ ],
+ "config": {
+ "label": "Check code quality with PHPMD"
+ }
+ },
+ {
+ "action": "npx editorconfig-checker {$STAGED_FILES}",
+ "config": {
+ "label": "Check EditorConfig compliance"
+ }
+ },
+ {
+ "action": "npx markdownlint-cli2 {$STAGED_FILES|of-type:md}",
+ "config": {
+ "label": "Lint Markdown files"
+ }
+ }
+ ]
+ },
+ "pre-push": {
+ "enabled": false,
+ "actions": []
+ },
+ "commit-msg": {
+ "enabled": false,
+ "actions": []
+ }
+}
diff --git a/composer.json b/composer.json
index d210dd8..1b65289 100644
--- a/composer.json
+++ b/composer.json
@@ -23,26 +23,28 @@
},
"require": {
"php": "^8.4",
- "guzzlehttp/guzzle": "^7",
"illuminate/http": "^12.17",
- "laravel-zero/phar-updater": "^1.2",
- "nunomaduro/termwind": "^2",
"spatie/data-transfer-object": "^3.7"
},
"require-dev": {
+ "captainhook/captainhook": "^5.28",
+ "captainhook/hook-installer": "^1.0",
"ergebnis/composer-normalize": "^2",
"laravel-zero/framework": "^12",
"mockery/mockery": "^1",
"pestphp/pest": "^4",
- "roave/security-advisories": "dev-latest"
+ "phpmd/phpmd": "^2.15",
+ "roave/security-advisories": "dev-latest",
+ "squizlabs/php_codesniffer": "^4.0"
+ },
+ "suggest": {
+ "ext-pcov": "Required for code coverage reporting (composer coverage)"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
- "App\\": "app/",
- "Database\\Factories\\": "database/factories/",
- "Database\\Seeders\\": "database/seeders/"
+ "App\\": "app/"
}
},
"autoload-dev": {
@@ -55,7 +57,7 @@
],
"config": {
"allow-plugins": {
- "dealerdirect/phpcodesniffer-composer-installer": true,
+ "captainhook/hook-installer": true,
"ergebnis/composer-normalize": true,
"pestphp/pest-plugin": true
},
@@ -72,6 +74,22 @@
"@php application app:build branch-usage-checker",
"rm -f application.phar || true"
],
+ "coverage": "php -d pcov.enabled=1 vendor/bin/pest --coverage",
+ "format": "vendor/bin/phpcbf || true",
+ "format:md": "npx markdown-table-formatter *.md docs/**/*.md",
+ "lint": "vendor/bin/phpcs",
+ "lint:all": [
+ "@lint",
+ "@lint:phpmd",
+ "@lint:md",
+ "@lint:ec"
+ ],
+ "lint:ec": "npx editorconfig-checker",
+ "lint:md": [
+ "npx markdownlint-cli2 '**/*.md' '#vendor' '#node_modules'",
+ "npx markdown-table-formatter --check *.md docs/**/*.md"
+ ],
+ "lint:phpmd": "vendor/bin/phpmd app,tests text phpmd.xml",
"test": "vendor/bin/pest",
"x": "@php builds/branch-usage-checker"
}
diff --git a/composer.lock b/composer.lock
index adb3c60..01e9c94 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "25bc97c24cf5394a1d670f4ba526e36d",
+ "content-hash": "cf1f39464e98b53a5308da63ab9b7094",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@@ -649,16 +649,16 @@
},
{
"name": "illuminate/collections",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/collections.git",
- "reference": "1fd7db2203ce5a935fffd2ad40955248fb9f581c"
+ "reference": "f35c084f0d9bc57895515cb4d0665797c66285fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/collections/zipball/1fd7db2203ce5a935fffd2ad40955248fb9f581c",
- "reference": "1fd7db2203ce5a935fffd2ad40955248fb9f581c",
+ "url": "https://api.github.com/repos/illuminate/collections/zipball/f35c084f0d9bc57895515cb4d0665797c66285fd",
+ "reference": "f35c084f0d9bc57895515cb4d0665797c66285fd",
"shasum": ""
},
"require": {
@@ -705,11 +705,11 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T13:43:38+00:00"
+ "time": "2026-02-16T14:10:38+00:00"
},
{
"name": "illuminate/conditionable",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/conditionable.git",
@@ -755,16 +755,16 @@
},
{
"name": "illuminate/contracts",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
- "reference": "3d4eeab332c04a9eaea90968c19a66f78745e47a"
+ "reference": "620d82bad07f55fcb7f5125f44c9d9b93335fb8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/contracts/zipball/3d4eeab332c04a9eaea90968c19a66f78745e47a",
- "reference": "3d4eeab332c04a9eaea90968c19a66f78745e47a",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/620d82bad07f55fcb7f5125f44c9d9b93335fb8c",
+ "reference": "620d82bad07f55fcb7f5125f44c9d9b93335fb8c",
"shasum": ""
},
"require": {
@@ -799,20 +799,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-01-28T15:26:27+00:00"
+ "time": "2026-02-10T18:16:44+00:00"
},
{
"name": "illuminate/filesystem",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/filesystem.git",
- "reference": "62c225e046a4c469779c0855dce7abd8c0b1fa1e"
+ "reference": "c4c3f8612f218afcf09f3c7f5c7dc9e282626800"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/filesystem/zipball/62c225e046a4c469779c0855dce7abd8c0b1fa1e",
- "reference": "62c225e046a4c469779c0855dce7abd8c0b1fa1e",
+ "url": "https://api.github.com/repos/illuminate/filesystem/zipball/c4c3f8612f218afcf09f3c7f5c7dc9e282626800",
+ "reference": "c4c3f8612f218afcf09f3c7f5c7dc9e282626800",
"shasum": ""
},
"require": {
@@ -866,20 +866,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T20:42:48+00:00"
+ "time": "2026-02-13T20:26:32+00:00"
},
{
"name": "illuminate/http",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/http.git",
- "reference": "3f764166fab80c79afc33b21e14c608aa7f67a72"
+ "reference": "90f63e595e913feccba8e6e5a77982291fd3b0cd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/http/zipball/3f764166fab80c79afc33b21e14c608aa7f67a72",
- "reference": "3f764166fab80c79afc33b21e14c608aa7f67a72",
+ "url": "https://api.github.com/repos/illuminate/http/zipball/90f63e595e913feccba8e6e5a77982291fd3b0cd",
+ "reference": "90f63e595e913feccba8e6e5a77982291fd3b0cd",
"shasum": ""
},
"require": {
@@ -928,11 +928,11 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T22:07:11+00:00"
+ "time": "2026-02-15T19:14:33+00:00"
},
{
"name": "illuminate/macroable",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/macroable.git",
@@ -978,7 +978,7 @@
},
{
"name": "illuminate/reflection",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/reflection.git",
@@ -1029,16 +1029,16 @@
},
{
"name": "illuminate/session",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/session.git",
- "reference": "4ee5e68ce5e1ee88f32f77694bc2e564c5809ffa"
+ "reference": "98802e67dd5e059c0b978b3fe8f5f0a3ac17ec4e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/session/zipball/4ee5e68ce5e1ee88f32f77694bc2e564c5809ffa",
- "reference": "4ee5e68ce5e1ee88f32f77694bc2e564c5809ffa",
+ "url": "https://api.github.com/repos/illuminate/session/zipball/98802e67dd5e059c0b978b3fe8f5f0a3ac17ec4e",
+ "reference": "98802e67dd5e059c0b978b3fe8f5f0a3ac17ec4e",
"shasum": ""
},
"require": {
@@ -1082,20 +1082,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T13:23:11+00:00"
+ "time": "2026-02-14T23:03:41+00:00"
},
{
"name": "illuminate/support",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/support.git",
- "reference": "fcc84cf4fba138f1835d6a5978ac4434f11a55aa"
+ "reference": "6e9de455bcb232372db11531b72a01d4eca83ef2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/support/zipball/fcc84cf4fba138f1835d6a5978ac4434f11a55aa",
- "reference": "fcc84cf4fba138f1835d6a5978ac4434f11a55aa",
+ "url": "https://api.github.com/repos/illuminate/support/zipball/6e9de455bcb232372db11531b72a01d4eca83ef2",
+ "reference": "6e9de455bcb232372db11531b72a01d4eca83ef2",
"shasum": ""
},
"require": {
@@ -1162,68 +1162,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-10T16:02:09+00:00"
- },
- {
- "name": "laravel-zero/phar-updater",
- "version": "v1.4.2",
- "source": {
- "type": "git",
- "url": "https://github.com/laravel-zero/phar-updater.git",
- "reference": "131bc5e7477c9233fab8087f7d0bbb234616b417"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/laravel-zero/phar-updater/zipball/131bc5e7477c9233fab8087f7d0bbb234616b417",
- "reference": "131bc5e7477c9233fab8087f7d0bbb234616b417",
- "shasum": ""
- },
- "require": {
- "php": "^8.2"
- },
- "conflict": {
- "padraic/phar-updater": "*"
- },
- "require-dev": {
- "ext-json": "*",
- "laravel/pint": "^1.21",
- "phpstan/phpstan": "^2.0",
- "phpunit/phpunit": "^11.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Humbug\\SelfUpdate\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Padraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "http://blog.astrumfutura.com"
- },
- {
- "name": "Owen Voke",
- "email": "development@voke.dev",
- "homepage": "https://voke.dev"
- }
- ],
- "description": "A thing to make PHAR self-updating easy and secure.",
- "keywords": [
- "humbug",
- "phar",
- "self-update",
- "update"
- ],
- "support": {
- "issues": "https://github.com/laravel-zero/phar-updater/issues",
- "source": "https://github.com/laravel-zero/phar-updater/tree/v1.4.2"
- },
- "time": "2025-04-07T12:28:11+00:00"
+ "time": "2026-02-14T23:03:41+00:00"
},
{
"name": "nesbot/carbon",
@@ -1330,93 +1269,6 @@
],
"time": "2026-01-29T09:26:29+00:00"
},
- {
- "name": "nunomaduro/termwind",
- "version": "v2.3.3",
- "source": {
- "type": "git",
- "url": "https://github.com/nunomaduro/termwind.git",
- "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017",
- "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017",
- "shasum": ""
- },
- "require": {
- "ext-mbstring": "*",
- "php": "^8.2",
- "symfony/console": "^7.3.6"
- },
- "require-dev": {
- "illuminate/console": "^11.46.1",
- "laravel/pint": "^1.25.1",
- "mockery/mockery": "^1.6.12",
- "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3",
- "phpstan/phpstan": "^1.12.32",
- "phpstan/phpstan-strict-rules": "^1.6.2",
- "symfony/var-dumper": "^7.3.5",
- "thecodingmachine/phpstan-strict-rules": "^1.0.0"
- },
- "type": "library",
- "extra": {
- "laravel": {
- "providers": [
- "Termwind\\Laravel\\TermwindServiceProvider"
- ]
- },
- "branch-alias": {
- "dev-2.x": "2.x-dev"
- }
- },
- "autoload": {
- "files": [
- "src/Functions.php"
- ],
- "psr-4": {
- "Termwind\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nuno Maduro",
- "email": "enunomaduro@gmail.com"
- }
- ],
- "description": "Its like Tailwind CSS, but for the console.",
- "keywords": [
- "cli",
- "console",
- "css",
- "package",
- "php",
- "style"
- ],
- "support": {
- "issues": "https://github.com/nunomaduro/termwind/issues",
- "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3"
- },
- "funding": [
- {
- "url": "https://www.paypal.com/paypalme/enunomaduro",
- "type": "custom"
- },
- {
- "url": "https://github.com/nunomaduro",
- "type": "github"
- },
- {
- "url": "https://github.com/xiCO2k",
- "type": "github"
- }
- ],
- "time": "2025-11-20T02:34:59+00:00"
- },
{
"name": "psr/clock",
"version": "1.0.0",
@@ -2014,104 +1866,6 @@
],
"time": "2025-11-12T15:46:48+00:00"
},
- {
- "name": "symfony/console",
- "version": "v7.4.4",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/console.git",
- "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894",
- "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894",
- "shasum": ""
- },
- "require": {
- "php": ">=8.2",
- "symfony/deprecation-contracts": "^2.5|^3",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^7.2|^8.0"
- },
- "conflict": {
- "symfony/dependency-injection": "<6.4",
- "symfony/dotenv": "<6.4",
- "symfony/event-dispatcher": "<6.4",
- "symfony/lock": "<6.4",
- "symfony/process": "<6.4"
- },
- "provide": {
- "psr/log-implementation": "1.0|2.0|3.0"
- },
- "require-dev": {
- "psr/log": "^1|^2|^3",
- "symfony/config": "^6.4|^7.0|^8.0",
- "symfony/dependency-injection": "^6.4|^7.0|^8.0",
- "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
- "symfony/http-foundation": "^6.4|^7.0|^8.0",
- "symfony/http-kernel": "^6.4|^7.0|^8.0",
- "symfony/lock": "^6.4|^7.0|^8.0",
- "symfony/messenger": "^6.4|^7.0|^8.0",
- "symfony/process": "^6.4|^7.0|^8.0",
- "symfony/stopwatch": "^6.4|^7.0|^8.0",
- "symfony/var-dumper": "^6.4|^7.0|^8.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Console\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Eases the creation of beautiful and testable command line interfaces",
- "homepage": "https://symfony.com",
- "keywords": [
- "cli",
- "command-line",
- "console",
- "terminal"
- ],
- "support": {
- "source": "https://github.com/symfony/console/tree/v7.4.4"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2026-01-13T11:36:38+00:00"
- },
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
@@ -2863,88 +2617,6 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
- {
- "name": "symfony/polyfill-intl-grapheme",
- "version": "v1.33.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
- "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2"
- },
- "suggest": {
- "ext-intl": "For best performance"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for intl's grapheme_* functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "grapheme",
- "intl",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2025-06-27T09:58:17+00:00"
- },
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.33.0",
@@ -3526,183 +3198,6 @@
],
"time": "2025-06-23T16:12:55+00:00"
},
- {
- "name": "symfony/service-contracts",
- "version": "v3.6.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/service-contracts.git",
- "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
- "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1",
- "psr/container": "^1.1|^2.0",
- "symfony/deprecation-contracts": "^2.5|^3"
- },
- "conflict": {
- "ext-psr": "<1.1|>=2"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/contracts",
- "name": "symfony/contracts"
- },
- "branch-alias": {
- "dev-main": "3.6-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Contracts\\Service\\": ""
- },
- "exclude-from-classmap": [
- "/Test/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Generic abstractions related to writing services",
- "homepage": "https://symfony.com",
- "keywords": [
- "abstractions",
- "contracts",
- "decoupling",
- "interfaces",
- "interoperability",
- "standards"
- ],
- "support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2025-07-15T11:30:57+00:00"
- },
- {
- "name": "symfony/string",
- "version": "v8.0.4",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/string.git",
- "reference": "758b372d6882506821ed666032e43020c4f57194"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194",
- "reference": "758b372d6882506821ed666032e43020c4f57194",
- "shasum": ""
- },
- "require": {
- "php": ">=8.4",
- "symfony/polyfill-ctype": "^1.8",
- "symfony/polyfill-intl-grapheme": "^1.33",
- "symfony/polyfill-intl-normalizer": "^1.0",
- "symfony/polyfill-mbstring": "^1.0"
- },
- "conflict": {
- "symfony/translation-contracts": "<2.5"
- },
- "require-dev": {
- "symfony/emoji": "^7.4|^8.0",
- "symfony/http-client": "^7.4|^8.0",
- "symfony/intl": "^7.4|^8.0",
- "symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^7.4|^8.0"
- },
- "type": "library",
- "autoload": {
- "files": [
- "Resources/functions.php"
- ],
- "psr-4": {
- "Symfony\\Component\\String\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
- "homepage": "https://symfony.com",
- "keywords": [
- "grapheme",
- "i18n",
- "string",
- "unicode",
- "utf-8",
- "utf8"
- ],
- "support": {
- "source": "https://github.com/symfony/string/tree/v8.0.4"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2026-01-12T12:37:40+00:00"
- },
{
"name": "symfony/translation",
"version": "v8.0.4",
@@ -4043,16 +3538,16 @@
"packages-dev": [
{
"name": "brianium/paratest",
- "version": "v7.17.0",
+ "version": "v7.19.0",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
- "reference": "53cb90a6aa3ef3840458781600628ade058a18b9"
+ "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paratestphp/paratest/zipball/53cb90a6aa3ef3840458781600628ade058a18b9",
- "reference": "53cb90a6aa3ef3840458781600628ade058a18b9",
+ "url": "https://api.github.com/repos/paratestphp/paratest/zipball/7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6",
+ "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6",
"shasum": ""
},
"require": {
@@ -4063,13 +3558,13 @@
"fidry/cpu-core-counter": "^1.3.0",
"jean85/pretty-package-versions": "^2.1.1",
"php": "~8.3.0 || ~8.4.0 || ~8.5.0",
- "phpunit/php-code-coverage": "^12.5.2",
- "phpunit/php-file-iterator": "^6",
- "phpunit/php-timer": "^8",
- "phpunit/phpunit": "^12.5.8",
- "sebastian/environment": "^8.0.3",
- "symfony/console": "^7.3.4 || ^8.0.0",
- "symfony/process": "^7.3.4 || ^8.0.0"
+ "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1",
+ "phpunit/php-file-iterator": "^6.0.1 || ^7",
+ "phpunit/php-timer": "^8 || ^9",
+ "phpunit/phpunit": "^12.5.9 || ^13",
+ "sebastian/environment": "^8.0.3 || ^9",
+ "symfony/console": "^7.4.4 || ^8.0.4",
+ "symfony/process": "^7.4.5 || ^8.0.5"
},
"require-dev": {
"doctrine/coding-standard": "^14.0.0",
@@ -4080,7 +3575,7 @@
"phpstan/phpstan-deprecation-rules": "^2.0.3",
"phpstan/phpstan-phpunit": "^2.0.12",
"phpstan/phpstan-strict-rules": "^2.0.8",
- "symfony/filesystem": "^7.3.2 || ^8.0.0"
+ "symfony/filesystem": "^7.4.0 || ^8.0.1"
},
"bin": [
"bin/paratest",
@@ -4120,7 +3615,7 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
- "source": "https://github.com/paratestphp/paratest/tree/v7.17.0"
+ "source": "https://github.com/paratestphp/paratest/tree/v7.19.0"
},
"funding": [
{
@@ -4132,7 +3627,7 @@
"type": "paypal"
}
],
- "time": "2026-02-05T09:14:44+00:00"
+ "time": "2026-02-06T10:53:26+00:00"
},
{
"name": "brick/math",
@@ -4194,6 +3689,338 @@
],
"time": "2026-02-10T14:33:43+00:00"
},
+ {
+ "name": "captainhook/captainhook",
+ "version": "5.28.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/captainhook-git/captainhook.git",
+ "reference": "5d35b249f3843ef36ead119f4347e649278ad6d8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/captainhook-git/captainhook/zipball/5d35b249f3843ef36ead119f4347e649278ad6d8",
+ "reference": "5d35b249f3843ef36ead119f4347e649278ad6d8",
+ "shasum": ""
+ },
+ "require": {
+ "captainhook/secrets": "^0.9.4",
+ "ext-json": "*",
+ "ext-spl": "*",
+ "ext-xml": "*",
+ "php": ">=8.0",
+ "sebastianfeldmann/camino": "^0.9.2",
+ "sebastianfeldmann/cli": "^3.3",
+ "sebastianfeldmann/git": "^3.16.0",
+ "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0",
+ "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0",
+ "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0"
+ },
+ "replace": {
+ "sebastianfeldmann/captainhook": "*"
+ },
+ "require-dev": {
+ "composer/composer": "~1 || ^2.0",
+ "mikey179/vfsstream": "~1"
+ },
+ "bin": [
+ "bin/captainhook"
+ ],
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "config": "captainhook.json"
+ },
+ "branch-alias": {
+ "dev-main": "6.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "CaptainHook\\App\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "PHP git hook manager",
+ "homepage": "https://php.captainhook.info/",
+ "keywords": [
+ "commit-msg",
+ "git",
+ "hooks",
+ "post-merge",
+ "pre-commit",
+ "pre-push",
+ "prepare-commit-msg"
+ ],
+ "support": {
+ "issues": "https://github.com/captainhook-git/captainhook/issues",
+ "source": "https://github.com/captainhook-git/captainhook/tree/5.28.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T14:08:58+00:00"
+ },
+ {
+ "name": "captainhook/hook-installer",
+ "version": "1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/captainhook-git/hook-installer.git",
+ "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/captainhook-git/hook-installer/zipball/fb3c45f6204b08baba999f4ffc4ae707bf684e8b",
+ "reference": "fb3c45f6204b08baba999f4ffc4ae707bf684e8b",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.1|^2.0",
+ "php": ">=8.0"
+ },
+ "require-dev": {
+ "composer/composer": "*"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "CaptainHook\\HookInstaller\\ComposerPlugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "CaptainHook\\HookInstaller\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "Composer Plugin that makes everyone activate the CaptainHook git hooks locally",
+ "support": {
+ "issues": "https://github.com/captainhook-git/hook-installer/issues",
+ "source": "https://github.com/captainhook-git/hook-installer/tree/1.0.4"
+ },
+ "time": "2025-04-08T07:12:26+00:00"
+ },
+ {
+ "name": "captainhook/secrets",
+ "version": "0.9.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/captainhook-git/secrets.git",
+ "reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/captainhook-git/secrets/zipball/d62c97f75f81ac98e22f1c282482bd35fa82f631",
+ "reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "CaptainHook\\Secrets\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "Utility classes to detect secrets",
+ "keywords": [
+ "commit-msg",
+ "keys",
+ "passwords",
+ "post-merge",
+ "prepare-commit-msg",
+ "secrets",
+ "tokens"
+ ],
+ "support": {
+ "issues": "https://github.com/captainhook-git/secrets/issues",
+ "source": "https://github.com/captainhook-git/secrets/tree/0.9.7"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-04-08T07:10:48+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-12T16:29:46+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without Xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-06T16:37:16+00:00"
+ },
{
"name": "doctrine/deprecations",
"version": "1.1.6",
@@ -5017,16 +4844,16 @@
},
{
"name": "illuminate/bus",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/bus.git",
- "reference": "0e19c4978558bd29000dfde28e4f1db2fb41b209"
+ "reference": "d1362729e3d37efca1f1cbc7a02f752cf24af442"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/bus/zipball/0e19c4978558bd29000dfde28e4f1db2fb41b209",
- "reference": "0e19c4978558bd29000dfde28e4f1db2fb41b209",
+ "url": "https://api.github.com/repos/illuminate/bus/zipball/d1362729e3d37efca1f1cbc7a02f752cf24af442",
+ "reference": "d1362729e3d37efca1f1cbc7a02f752cf24af442",
"shasum": ""
},
"require": {
@@ -5066,20 +4893,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-05T14:28:47+00:00"
+ "time": "2026-02-12T15:22:19+00:00"
},
{
"name": "illuminate/cache",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/cache.git",
- "reference": "a7f0de6a2cff1948a09f3b4a0531f9e363447674"
+ "reference": "58aee25d94f3376eb15f40e1cc5963c6c8b37800"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/cache/zipball/a7f0de6a2cff1948a09f3b4a0531f9e363447674",
- "reference": "a7f0de6a2cff1948a09f3b4a0531f9e363447674",
+ "url": "https://api.github.com/repos/illuminate/cache/zipball/58aee25d94f3376eb15f40e1cc5963c6c8b37800",
+ "reference": "58aee25d94f3376eb15f40e1cc5963c6c8b37800",
"shasum": ""
},
"require": {
@@ -5095,7 +4922,7 @@
"suggest": {
"ext-apcu": "Required to use the APC cache driver.",
"ext-filter": "Required to use the DynamoDb cache driver.",
- "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-memcached": "Required to use the memcached cache driver.",
"illuminate/database": "Required to use the database cache driver (^12.0).",
"illuminate/filesystem": "Required to use the file cache driver (^12.0).",
"illuminate/redis": "Required to use the redis cache driver (^12.0).",
@@ -5128,11 +4955,11 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T20:42:48+00:00"
+ "time": "2026-02-16T18:49:43+00:00"
},
{
"name": "illuminate/config",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/config.git",
@@ -5180,16 +5007,16 @@
},
{
"name": "illuminate/console",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/console.git",
- "reference": "edd1d00fe086108b80d5c095aba72a58f52d2d3b"
+ "reference": "275125420a7505c193d718efc934f91f83e7a8fe"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/console/zipball/edd1d00fe086108b80d5c095aba72a58f52d2d3b",
- "reference": "edd1d00fe086108b80d5c095aba72a58f52d2d3b",
+ "url": "https://api.github.com/repos/illuminate/console/zipball/275125420a7505c193d718efc934f91f83e7a8fe",
+ "reference": "275125420a7505c193d718efc934f91f83e7a8fe",
"shasum": ""
},
"require": {
@@ -5242,20 +5069,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-10T16:01:17+00:00"
+ "time": "2026-02-12T15:23:36+00:00"
},
{
"name": "illuminate/container",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/container.git",
- "reference": "a605f25d0e014b6e521bcb142a4eba578966a24f"
+ "reference": "648307e8f54bcd9450c858f99abd11bc50c364a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/container/zipball/a605f25d0e014b6e521bcb142a4eba578966a24f",
- "reference": "a605f25d0e014b6e521bcb142a4eba578966a24f",
+ "url": "https://api.github.com/repos/illuminate/container/zipball/648307e8f54bcd9450c858f99abd11bc50c364a0",
+ "reference": "648307e8f54bcd9450c858f99abd11bc50c364a0",
"shasum": ""
},
"require": {
@@ -5304,11 +5131,11 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-01-29T03:13:58+00:00"
+ "time": "2026-02-12T16:13:27+00:00"
},
{
"name": "illuminate/events",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/events.git",
@@ -5363,7 +5190,7 @@
},
{
"name": "illuminate/pipeline",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/pipeline.git",
@@ -5415,7 +5242,7 @@
},
{
"name": "illuminate/process",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/process.git",
@@ -5466,16 +5293,16 @@
},
{
"name": "illuminate/testing",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/testing.git",
- "reference": "021b86baa1e8da0f8c75b47a94cdf664c134feca"
+ "reference": "41ed43b5b51f0606a25b0162b922c17d1fb9bc03"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/testing/zipball/021b86baa1e8da0f8c75b47a94cdf664c134feca",
- "reference": "021b86baa1e8da0f8c75b47a94cdf664c134feca",
+ "url": "https://api.github.com/repos/illuminate/testing/zipball/41ed43b5b51f0606a25b0162b922c17d1fb9bc03",
+ "reference": "41ed43b5b51f0606a25b0162b922c17d1fb9bc03",
"shasum": ""
},
"require": {
@@ -5522,20 +5349,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-09T13:46:06+00:00"
+ "time": "2026-02-16T22:49:32+00:00"
},
{
"name": "illuminate/view",
- "version": "v12.51.0",
+ "version": "v12.52.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/view.git",
- "reference": "5b3d7ae07665b98cca46846074edc008ed5e3864"
+ "reference": "dc0106f1f09d3bffdfba1c6a637b01d00f0fc4ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/illuminate/view/zipball/5b3d7ae07665b98cca46846074edc008ed5e3864",
- "reference": "5b3d7ae07665b98cca46846074edc008ed5e3864",
+ "url": "https://api.github.com/repos/illuminate/view/zipball/dc0106f1f09d3bffdfba1c6a637b01d00f0fc4ba",
+ "reference": "dc0106f1f09d3bffdfba1c6a637b01d00f0fc4ba",
"shasum": ""
},
"require": {
@@ -5576,7 +5403,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-01-20T15:07:29+00:00"
+ "time": "2026-02-14T22:58:48+00:00"
},
{
"name": "jean85/pretty-package-versions",
@@ -6582,39 +6409,36 @@
},
{
"name": "nunomaduro/collision",
- "version": "v8.8.3",
+ "version": "v8.9.1",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
- "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4"
+ "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4",
- "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/a1ed3fa530fd60bc515f9303e8520fcb7d4bd935",
+ "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935",
"shasum": ""
},
"require": {
- "filp/whoops": "^2.18.1",
- "nunomaduro/termwind": "^2.3.1",
+ "filp/whoops": "^2.18.4",
+ "nunomaduro/termwind": "^2.4.0",
"php": "^8.2.0",
- "symfony/console": "^7.3.0"
+ "symfony/console": "^7.4.4 || ^8.0.4"
},
"conflict": {
- "laravel/framework": "<11.44.2 || >=13.0.0",
- "phpunit/phpunit": "<11.5.15 || >=13.0.0"
+ "laravel/framework": "<11.48.0 || >=14.0.0",
+ "phpunit/phpunit": "<11.5.50 || >=14.0.0"
},
"require-dev": {
- "brianium/paratest": "^7.8.3",
- "larastan/larastan": "^3.4.2",
- "laravel/framework": "^11.44.2 || ^12.18",
- "laravel/pint": "^1.22.1",
- "laravel/sail": "^1.43.1",
- "laravel/sanctum": "^4.1.1",
- "laravel/tinker": "^2.10.1",
- "orchestra/testbench-core": "^9.12.0 || ^10.4",
- "pestphp/pest": "^3.8.2 || ^4.0.0",
- "sebastian/environment": "^7.2.1 || ^8.0"
+ "brianium/paratest": "^7.8.5",
+ "larastan/larastan": "^3.9.2",
+ "laravel/framework": "^11.48.0 || ^12.52.0",
+ "laravel/pint": "^1.27.1",
+ "orchestra/testbench-core": "^9.12.0 || ^10.9.0",
+ "pestphp/pest": "^3.8.5 || ^4.4.1 || ^5.0.0",
+ "sebastian/environment": "^7.2.1 || ^8.0.3 || ^9.0.0"
},
"type": "library",
"extra": {
@@ -6677,7 +6501,7 @@
"type": "patreon"
}
],
- "time": "2025-11-20T02:55:25+00:00"
+ "time": "2026-02-17T17:33:08+00:00"
},
{
"name": "nunomaduro/laravel-console-summary",
@@ -6874,42 +6698,192 @@
"time": "2025-02-19T11:22:09+00:00"
},
{
- "name": "pestphp/pest",
- "version": "v4.3.2",
+ "name": "nunomaduro/termwind",
+ "version": "v2.4.0",
"source": {
"type": "git",
- "url": "https://github.com/pestphp/pest.git",
- "reference": "3a4329ddc7a2b67c19fca8342a668b39be3ae398"
+ "url": "https://github.com/nunomaduro/termwind.git",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pestphp/pest/zipball/3a4329ddc7a2b67c19fca8342a668b39be3ae398",
- "reference": "3a4329ddc7a2b67c19fca8342a668b39be3ae398",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8",
"shasum": ""
},
"require": {
- "brianium/paratest": "^7.16.1",
- "nunomaduro/collision": "^8.8.3",
- "nunomaduro/termwind": "^2.3.3",
+ "ext-mbstring": "*",
+ "php": "^8.2",
+ "symfony/console": "^7.4.4 || ^8.0.4"
+ },
+ "require-dev": {
+ "illuminate/console": "^11.47.0",
+ "laravel/pint": "^1.27.1",
+ "mockery/mockery": "^1.6.12",
+ "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2",
+ "phpstan/phpstan": "^1.12.32",
+ "phpstan/phpstan-strict-rules": "^1.6.2",
+ "symfony/var-dumper": "^7.3.5 || ^8.0.4",
+ "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Termwind\\Laravel\\TermwindServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Termwind\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "It's like Tailwind CSS, but for the console.",
+ "keywords": [
+ "cli",
+ "console",
+ "css",
+ "package",
+ "php",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/termwind/issues",
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/xiCO2k",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T23:10:27+00:00"
+ },
+ {
+ "name": "pdepend/pdepend",
+ "version": "2.16.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pdepend/pdepend.git",
+ "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58",
+ "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.7",
+ "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0",
+ "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0",
+ "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0",
+ "symfony/polyfill-mbstring": "^1.19"
+ },
+ "require-dev": {
+ "easy-doc/easy-doc": "0.0.0|^1.2.3",
+ "gregwar/rst": "^1.0",
+ "squizlabs/php_codesniffer": "^2.0.0"
+ },
+ "bin": [
+ "src/bin/pdepend"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PDepend\\": "src/main/php/PDepend"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Official version of pdepend to be handled with Composer",
+ "keywords": [
+ "PHP Depend",
+ "PHP_Depend",
+ "dev",
+ "pdepend"
+ ],
+ "support": {
+ "issues": "https://github.com/pdepend/pdepend/issues",
+ "source": "https://github.com/pdepend/pdepend/tree/2.16.2"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-17T18:09:59+00:00"
+ },
+ {
+ "name": "pestphp/pest",
+ "version": "v4.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest.git",
+ "reference": "f96a1b27864b585b0b29b0ee7331176726f7e54a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest/zipball/f96a1b27864b585b0b29b0ee7331176726f7e54a",
+ "reference": "f96a1b27864b585b0b29b0ee7331176726f7e54a",
+ "shasum": ""
+ },
+ "require": {
+ "brianium/paratest": "^7.19.0",
+ "nunomaduro/collision": "^8.9.0",
+ "nunomaduro/termwind": "^2.4.0",
"pestphp/pest-plugin": "^4.0.0",
"pestphp/pest-plugin-arch": "^4.0.0",
"pestphp/pest-plugin-mutate": "^4.0.1",
"pestphp/pest-plugin-profanity": "^4.2.1",
"php": "^8.3.0",
- "phpunit/phpunit": "^12.5.8",
- "symfony/process": "^7.4.4|^8.0.0"
+ "phpunit/phpunit": "^12.5.12",
+ "symfony/process": "^7.4.5|^8.0.5"
},
"conflict": {
"filp/whoops": "<2.18.3",
- "phpunit/phpunit": ">12.5.8",
+ "phpunit/phpunit": ">12.5.12",
"sebastian/exporter": "<7.0.0",
"webmozart/assert": "<1.11.0"
},
"require-dev": {
- "pestphp/pest-dev-tools": "^4.0.0",
- "pestphp/pest-plugin-browser": "^4.2.1",
+ "pestphp/pest-dev-tools": "^4.1.0",
+ "pestphp/pest-plugin-browser": "^4.3.0",
"pestphp/pest-plugin-type-coverage": "^4.0.3",
- "psy/psysh": "^0.12.18"
+ "psy/psysh": "^0.12.20"
},
"bin": [
"bin/pest"
@@ -6975,7 +6949,7 @@
],
"support": {
"issues": "https://github.com/pestphp/pest/issues",
- "source": "https://github.com/pestphp/pest/tree/v4.3.2"
+ "source": "https://github.com/pestphp/pest/tree/v4.4.1"
},
"funding": [
{
@@ -6987,7 +6961,7 @@
"type": "github"
}
],
- "time": "2026-01-28T01:01:19+00:00"
+ "time": "2026-02-17T15:27:18+00:00"
},
{
"name": "pestphp/pest-plugin",
@@ -7554,6 +7528,89 @@
},
"time": "2025-11-21T15:09:14+00:00"
},
+ {
+ "name": "phpmd/phpmd",
+ "version": "2.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpmd/phpmd.git",
+ "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0",
+ "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0",
+ "shasum": ""
+ },
+ "require": {
+ "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0",
+ "ext-xml": "*",
+ "pdepend/pdepend": "^2.16.1",
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "easy-doc/easy-doc": "0.0.0 || ^1.3.2",
+ "ext-json": "*",
+ "ext-simplexml": "*",
+ "gregwar/rst": "^1.0",
+ "mikey179/vfsstream": "^1.6.8",
+ "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2"
+ },
+ "bin": [
+ "src/bin/phpmd"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "PHPMD\\": "src/main/php"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Manuel Pichler",
+ "email": "github@manuel-pichler.de",
+ "homepage": "https://github.com/manuelpichler",
+ "role": "Project Founder"
+ },
+ {
+ "name": "Marc Würth",
+ "email": "ravage@bluewin.ch",
+ "homepage": "https://github.com/ravage84",
+ "role": "Project Maintainer"
+ },
+ {
+ "name": "Other contributors",
+ "homepage": "https://github.com/phpmd/phpmd/graphs/contributors",
+ "role": "Contributors"
+ }
+ ],
+ "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.",
+ "homepage": "https://phpmd.org/",
+ "keywords": [
+ "dev",
+ "mess detection",
+ "mess detector",
+ "pdepend",
+ "phpmd",
+ "pmd"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/phpmd",
+ "issues": "https://github.com/phpmd/phpmd/issues",
+ "source": "https://github.com/phpmd/phpmd/tree/2.15.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-11T08:22:20+00:00"
+ },
{
"name": "phpoption/phpoption",
"version": "1.9.5",
@@ -8024,16 +8081,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "12.5.8",
+ "version": "12.5.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "37ddb96c14bfee10304825edbb7e66d341ec6889"
+ "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/37ddb96c14bfee10304825edbb7e66d341ec6889",
- "reference": "37ddb96c14bfee10304825edbb7e66d341ec6889",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/418e06b3b46b0d54bad749ff4907fc7dfb530199",
+ "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199",
"shasum": ""
},
"require": {
@@ -8047,8 +8104,8 @@
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.3",
- "phpunit/php-code-coverage": "^12.5.2",
- "phpunit/php-file-iterator": "^6.0.0",
+ "phpunit/php-code-coverage": "^12.5.3",
+ "phpunit/php-file-iterator": "^6.0.1",
"phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0",
"phpunit/php-timer": "^8.0.0",
@@ -8059,6 +8116,7 @@
"sebastian/exporter": "^7.0.2",
"sebastian/global-state": "^8.0.2",
"sebastian/object-enumerator": "^7.0.0",
+ "sebastian/recursion-context": "^7.0.1",
"sebastian/type": "^6.0.3",
"sebastian/version": "^6.0.0",
"staabm/side-effects-detector": "^1.0.5"
@@ -8101,7 +8159,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.8"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.12"
},
"funding": [
{
@@ -8125,7 +8183,7 @@
"type": "tidelift"
}
],
- "time": "2026-01-27T06:12:29+00:00"
+ "time": "2026-02-16T08:34:36+00:00"
},
{
"name": "ramsey/collection",
@@ -8287,12 +8345,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "7f3e95c9ebf1b16e002dd2c913d30d962c2a6a16"
+ "reference": "92c5ec5685cfbcd7ef721a502e6622516728011c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7f3e95c9ebf1b16e002dd2c913d30d962c2a6a16",
- "reference": "7f3e95c9ebf1b16e002dd2c913d30d962c2a6a16",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/92c5ec5685cfbcd7ef721a502e6622516728011c",
+ "reference": "92c5ec5685cfbcd7ef721a502e6622516728011c",
"shasum": ""
},
"conflict": {
@@ -8460,6 +8518,7 @@
"devgroup/dotplant": "<2020.09.14-dev",
"digimix/wp-svg-upload": "<=1",
"directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2",
+ "directorytree/imapengine": "<1.22.3",
"dl/yag": "<3.0.1",
"dmk/webkitpdf": "<1.1.4",
"dnadesign/silverstripe-elemental": "<5.3.12",
@@ -8562,7 +8621,7 @@
"filegator/filegator": "<7.8",
"filp/whoops": "<2.1.13",
"fineuploader/php-traditional-server": "<=1.2.2",
- "firebase/php-jwt": "<6",
+ "firebase/php-jwt": "<7",
"fisharebest/webtrees": "<=2.1.18",
"fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2",
"fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6",
@@ -8600,7 +8659,7 @@
"genix/cms": "<=1.1.11",
"georgringer/news": "<1.3.3",
"geshi/geshi": "<=1.0.9.1",
- "getformwork/formwork": "<2.2",
+ "getformwork/formwork": "<=2.3.3",
"getgrav/grav": "<1.11.0.0-beta1",
"getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1",
"getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
@@ -8723,7 +8782,7 @@
"leantime/leantime": "<3.3",
"lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3",
"libreform/libreform": ">=2,<=2.0.8",
- "librenms/librenms": "<25.12",
+ "librenms/librenms": "<26.2",
"liftkit/database": "<2.13.2",
"lightsaml/lightsaml": "<1.3.5",
"limesurvey/limesurvey": "<6.5.12",
@@ -8931,7 +8990,7 @@
"propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7",
"propel/propel1": ">=1,<=1.7.1",
"psy/psysh": "<=0.11.22|>=0.12,<=0.12.18",
- "pterodactyl/panel": "<1.12",
+ "pterodactyl/panel": "<1.12.1",
"ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2",
"ptrofimov/beanstalk_console": "<1.7.14",
"pubnub/pubnub": "<6.1",
@@ -9034,7 +9093,7 @@
"starcitizentools/short-description": ">=4,<4.0.1",
"starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1",
"starcitizenwiki/embedvideo": "<=4",
- "statamic/cms": "<5.73.6|>=6,<6.2.5",
+ "statamic/cms": "<5.73.9|>=6,<6.3.2",
"stormpath/sdk": "<9.9.99",
"studio-42/elfinder": "<=2.1.64",
"studiomitte/friendlycaptcha": "<0.1.4",
@@ -9206,7 +9265,7 @@
"wpanel/wpanel4-cms": "<=4.3.1",
"wpcloud/wp-stateless": "<3.2",
"wpglobus/wpglobus": "<=1.9.6",
- "wwbn/avideo": "<14.3",
+ "wwbn/avideo": "<21",
"xataface/xataface": "<3",
"xpressengine/xpressengine": "<3.0.15",
"yab/quarx": "<2.4.5",
@@ -9265,7 +9324,8 @@
"zf-commons/zfc-user": "<1.2.2",
"zfcampus/zf-apigility-doctrine": ">=1,<1.0.3",
"zfr/zfr-oauth2-server-module": "<0.1.2",
- "zoujingli/thinkadmin": "<=6.1.53"
+ "zoujingli/thinkadmin": "<=6.1.53",
+ "zumba/json-serializer": "<3.2.3"
},
"default-branch": true,
"type": "metapackage",
@@ -9303,7 +9363,7 @@
"type": "tidelift"
}
],
- "time": "2026-02-13T23:11:21+00:00"
+ "time": "2026-02-20T22:06:39+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -10202,6 +10262,261 @@
],
"time": "2025-02-07T05:00:38+00:00"
},
+ {
+ "name": "sebastianfeldmann/camino",
+ "version": "0.9.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianfeldmann/camino.git",
+ "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianfeldmann/camino/zipball/bf2e4c8b2a029e9eade43666132b61331e3e8184",
+ "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SebastianFeldmann\\Camino\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "Path management the OO way",
+ "homepage": "https://github.com/sebastianfeldmann/camino",
+ "keywords": [
+ "file system",
+ "path"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianfeldmann/camino/issues",
+ "source": "https://github.com/sebastianfeldmann/camino/tree/0.9.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-01-03T13:15:10+00:00"
+ },
+ {
+ "name": "sebastianfeldmann/cli",
+ "version": "3.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianfeldmann/cli.git",
+ "reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianfeldmann/cli/zipball/6fa122afd528dae7d7ec988a604aa6c600f5d9b5",
+ "reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "require-dev": {
+ "symfony/process": "^4.3 | ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SebastianFeldmann\\Cli\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "PHP cli helper classes",
+ "homepage": "https://github.com/sebastianfeldmann/cli",
+ "keywords": [
+ "cli"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianfeldmann/cli/issues",
+ "source": "https://github.com/sebastianfeldmann/cli/tree/3.4.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-11-26T10:19:01+00:00"
+ },
+ {
+ "name": "sebastianfeldmann/git",
+ "version": "3.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianfeldmann/git.git",
+ "reference": "40a5cc043f0957228767f639e370ec92590e940f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/40a5cc043f0957228767f639e370ec92590e940f",
+ "reference": "40a5cc043f0957228767f639e370ec92590e940f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-simplexml": "*",
+ "php": ">=8.0",
+ "sebastianfeldmann/cli": "^3.0"
+ },
+ "require-dev": {
+ "mikey179/vfsstream": "^1.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SebastianFeldmann\\Git\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Feldmann",
+ "email": "sf@sebastian-feldmann.info"
+ }
+ ],
+ "description": "PHP git wrapper",
+ "homepage": "https://github.com/sebastianfeldmann/git",
+ "keywords": [
+ "git"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianfeldmann/git/issues",
+ "source": "https://github.com/sebastianfeldmann/git/tree/3.16.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianfeldmann",
+ "type": "github"
+ }
+ ],
+ "time": "2026-01-26T20:59:18+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "0525c73950de35ded110cffafb9892946d7771b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5",
+ "reference": "0525c73950de35ded110cffafb9892946d7771b5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=7.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-11-10T16:43:36+00:00"
+ },
{
"name": "staabm/side-effects-detector",
"version": "1.0.5",
@@ -10254,6 +10569,419 @@
],
"time": "2024-10-20T05:08:20+00:00"
},
+ {
+ "name": "symfony/config",
+ "version": "v7.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "4275b53b8ab0cf37f48bf273dc2285c8178efdfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/4275b53b8ab0cf37f48bf273dc2285c8178efdfb",
+ "reference": "4275b53b8ab0cf37f48bf273dc2285c8178efdfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/filesystem": "^7.1|^8.0",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "symfony/finder": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Config\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/config/tree/v7.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-13T11:36:38+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894",
+ "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/lock": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-13T11:36:38+00:00"
+ },
+ {
+ "name": "symfony/dependency-injection",
+ "version": "v7.4.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dependency-injection.git",
+ "reference": "76a02cddca45a5254479ad68f9fa274ead0a7ef2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/76a02cddca45a5254479ad68f9fa274ead0a7ef2",
+ "reference": "76a02cddca45a5254479ad68f9fa274ead0a7ef2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/service-contracts": "^3.6",
+ "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2",
+ "symfony/config": "<6.4",
+ "symfony/finder": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "symfony/service-implementation": "1.1|2.0|3.0"
+ },
+ "require-dev": {
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DependencyInjection\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows you to standardize and centralize the way objects are constructed in your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dependency-injection/tree/v7.4.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-27T16:16:02+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "d551b38811096d0be9c4691d406991b47c0c630a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a",
+ "reference": "d551b38811096d0be9c4691d406991b47c0c630a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-27T13:27:24+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-27T09:58:17+00:00"
+ },
{
"name": "symfony/process",
"version": "v7.4.5",
@@ -10320,24 +11048,281 @@
"time": "2026-01-26T15:07:59+00:00"
},
{
- "name": "ta-tikoma/phpunit-architecture-test",
- "version": "0.8.6",
+ "name": "symfony/service-contracts",
+ "version": "v3.6.1",
"source": {
"type": "git",
- "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git",
- "reference": "ad48430b92901fd7d003fdaf2d7b139f96c0906e"
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/ad48430b92901fd7d003fdaf2d7b139f96c0906e",
- "reference": "ad48430b92901fd7d003fdaf2d7b139f96c0906e",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
+ "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-15T11:30:57+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v8.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "758b372d6882506821ed666032e43020c4f57194"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194",
+ "reference": "758b372d6882506821ed666032e43020c4f57194",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "symfony/polyfill-ctype": "^1.8",
+ "symfony/polyfill-intl-grapheme": "^1.33",
+ "symfony/polyfill-intl-normalizer": "^1.0",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.4|^8.0",
+ "symfony/http-client": "^7.4|^8.0",
+ "symfony/intl": "^7.4|^8.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v8.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-12T12:37:40+00:00"
+ },
+ {
+ "name": "symfony/var-exporter",
+ "version": "v8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-exporter.git",
+ "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04",
+ "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4"
+ },
+ "require-dev": {
+ "symfony/property-access": "^7.4|^8.0",
+ "symfony/serializer": "^7.4|^8.0",
+ "symfony/var-dumper": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\VarExporter\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clone",
+ "construct",
+ "export",
+ "hydrate",
+ "instantiate",
+ "lazy-loading",
+ "proxy",
+ "serialize"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-exporter/tree/v8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-05T18:53:00+00:00"
+ },
+ {
+ "name": "ta-tikoma/phpunit-architecture-test",
+ "version": "0.8.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git",
+ "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8",
+ "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.18.0 || ^5.0.0",
"php": "^8.1.0",
"phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0",
- "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0",
+ "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0",
"symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0"
},
"require-dev": {
@@ -10374,9 +11359,9 @@
],
"support": {
"issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues",
- "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.6"
+ "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7"
},
- "time": "2026-01-30T07:16:00+00:00"
+ "time": "2026-02-17T17:25:14+00:00"
},
{
"name": "theseer/tokenizer",
@@ -10514,16 +11499,16 @@
},
{
"name": "webmozart/assert",
- "version": "2.1.3",
+ "version": "2.1.5",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "6976757ba8dd70bf8cbaea0914ad84d8b51a9f46"
+ "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/6976757ba8dd70bf8cbaea0914ad84d8b51a9f46",
- "reference": "6976757ba8dd70bf8cbaea0914ad84d8b51a9f46",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/79155f94852fa27e2f73b459f6503f5e87e2c188",
+ "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188",
"shasum": ""
},
"require": {
@@ -10570,9 +11555,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
- "source": "https://github.com/webmozarts/assert/tree/2.1.3"
+ "source": "https://github.com/webmozarts/assert/tree/2.1.5"
},
- "time": "2026-02-13T21:01:40+00:00"
+ "time": "2026-02-18T14:09:36+00:00"
}
],
"aliases": [],
diff --git a/config/commands.php b/config/commands.php
index 7678731..9e01bd8 100644
--- a/config/commands.php
+++ b/config/commands.php
@@ -53,7 +53,6 @@ return [
* below a list of commands that you don't to see in your app.
*/
'remove' => [
- \App\Commands\InspireCommand::class,
Illuminate\Console\Scheduling\ScheduleRunCommand::class,
Illuminate\Console\Scheduling\ScheduleListCommand::class,
Illuminate\Console\Scheduling\ScheduleFinishCommand::class,
diff --git a/docs/plans/2026-02-23-fix-sonar-duplications-design.md b/docs/plans/2026-02-23-fix-sonar-duplications-design.md
new file mode 100644
index 0000000..85e9391
--- /dev/null
+++ b/docs/plans/2026-02-23-fix-sonar-duplications-design.md
@@ -0,0 +1,57 @@
+# Fix SonarCloud Duplication Quality Gate
+
+## Problem
+
+PR #43 fails the Sonar Way quality gate: `new_duplicated_lines_density` is
+7.5% (threshold: <=3%). SonarCloud detects 2 duplicated blocks / 28 lines,
+all within `tests/Feature/Commands/CheckCommandTest.php`.
+
+The duplication is between 4 structurally identical input-validation tests
+(lines 71-93) that each follow the same 3-line pattern:
+
+```php
+$this->artisan('')
+ ->expectsOutputToContain('')
+ ->assertExitCode(1);
+```
+
+## Solution
+
+Replace the 4 separate tests with one Pest parameterized test using a named
+`with()` dataset:
+
+```php
+test('rejects invalid input', function (
+ string $args,
+ string $expected,
+) {
+ $this->artisan($args)
+ ->expectsOutputToContain($expected)
+ ->assertExitCode(1);
+})->with([
+ 'missing package' => [
+ 'check ivuorinen',
+ 'Missing package name',
+ ],
+ 'conflicting arguments' => [
+ 'check ivuorinen/branch-usage-checker extra',
+ 'Conflicting arguments',
+ ],
+ 'invalid vendor' => [
+ 'check INVALID!/package-name',
+ 'Invalid vendor name',
+ ],
+ 'invalid package' => [
+ 'check valid-vendor INVALID!',
+ 'Invalid package name',
+ ],
+]);
+```
+
+## Impact
+
+- **File:** `tests/Feature/Commands/CheckCommandTest.php`
+- **Lines removed:** ~24 (4 test blocks)
+- **Lines added:** ~10 (1 parameterized test)
+- **Test count:** Stays at 14 (Pest expands each dataset row)
+- **Expected duplication:** 0% on new code
diff --git a/docs/plans/2026-02-23-fix-sonar-duplications-plan.md b/docs/plans/2026-02-23-fix-sonar-duplications-plan.md
new file mode 100644
index 0000000..936a0f6
--- /dev/null
+++ b/docs/plans/2026-02-23-fix-sonar-duplications-plan.md
@@ -0,0 +1,135 @@
+# Fix SonarCloud Duplication Quality Gate — Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use
+> superpowers:executing-plans to implement this
+> plan task-by-task.
+
+**Goal:** Eliminate test code duplication that
+fails the Sonar Way quality gate on PR #43.
+
+**Architecture:** Replace 4 structurally identical
+input-validation tests with one Pest parameterized
+test using a named `with()` dataset. No production
+code changes.
+
+**Tech Stack:** Pest v4, PHP 8.4
+
+---
+
+## Task 1: Replace validation tests with dataset
+
+Files to modify:
+`tests/Feature/Commands/CheckCommandTest.php:71-93`
+
+### Step 1: Replace the 4 test blocks
+
+Replace these 4 tests (lines 71-93):
+
+```php
+test('check command with missing package shows error',
+ function () {
+ $this->artisan('check ivuorinen')
+ ->expectsOutputToContain('Missing package name')
+ ->assertExitCode(1);
+});
+
+test('check command with conflicting arguments shows error',
+ function () {
+ $this->artisan(
+ 'check ivuorinen/branch-usage-checker extra'
+ )
+ ->expectsOutputToContain(
+ 'Conflicting arguments'
+ )
+ ->assertExitCode(1);
+});
+
+test('check command with invalid vendor shows error',
+ function () {
+ $this->artisan('check INVALID!/package-name')
+ ->expectsOutputToContain('Invalid vendor name')
+ ->assertExitCode(1);
+});
+
+test('check command with invalid package name shows error',
+ function () {
+ $this->artisan('check valid-vendor INVALID!')
+ ->expectsOutputToContain(
+ 'Invalid package name'
+ )
+ ->assertExitCode(1);
+});
+```
+
+With one parameterized test:
+
+```php
+test('check command rejects invalid input',
+ function (string $args, string $expected) {
+ $this->artisan($args)
+ ->expectsOutputToContain($expected)
+ ->assertExitCode(1);
+})->with([
+ 'missing package' => [
+ 'check ivuorinen',
+ 'Missing package name',
+ ],
+ 'conflicting arguments' => [
+ 'check ivuorinen/branch-usage-checker extra',
+ 'Conflicting arguments',
+ ],
+ 'invalid vendor' => [
+ 'check INVALID!/package-name',
+ 'Invalid vendor name',
+ ],
+ 'invalid package' => [
+ 'check valid-vendor INVALID!',
+ 'Invalid package name',
+ ],
+]);
+```
+
+### Step 2: Run tests to verify all pass
+
+Run: `composer test`
+
+Expected: 14 passed (Pest expands dataset rows
+into individual test runs, so `with missing
+package`, `with conflicting arguments`, etc. each
+appear separately).
+
+### Step 3: Run linter
+
+Run: `composer lint`
+
+Expected: Clean (no PHPCS errors).
+
+### Step 4: Commit
+
+```bash
+git add tests/Feature/Commands/CheckCommandTest.php
+git commit -m "refactor(tests): parameterize \
+input-validation tests to fix duplication gate"
+```
+
+## Task 2: Push and verify quality gate
+
+### Step 1: Push
+
+```bash
+git push
+```
+
+### Step 2: Verify on SonarCloud
+
+Check the quality gate status via API:
+
+```bash
+curl -s 'https://sonarcloud.io/api/qualitygates/\
+project_status?projectKey=\
+ivuorinen_branch-usage-checker&pullRequest=43' \
+| python3 -m json.tool
+```
+
+Expected: `new_duplicated_lines_density` condition
+status changes from `ERROR` to `OK`.
diff --git a/docs/plans/2026-02-23-phpcs-captainhook-design.md b/docs/plans/2026-02-23-phpcs-captainhook-design.md
new file mode 100644
index 0000000..7fe8bcd
--- /dev/null
+++ b/docs/plans/2026-02-23-phpcs-captainhook-design.md
@@ -0,0 +1,49 @@
+# PHPCS + PHPCBF + CaptainHook Integration
+
+Date: 2026-02-23
+
+## Goal
+
+Add local code formatting enforcement via PHPCS/PHPCBF
+with automatic pre-commit hooks managed by CaptainHook.
+
+## New Dependencies
+
+- `squizlabs/php_codesniffer` (dev) — linter (`phpcs`)
+ and auto-fixer (`phpcbf`)
+- `captainhook/captainhook` (dev) — git hook manager
+- `captainhook/hook-installer` (dev) — Composer plugin
+ that auto-installs hooks on `composer install`
+
+## Composer Scripts
+
+- `composer lint` — runs `phpcs` to report violations
+- `composer format` — runs `phpcbf` to auto-fix
+
+## Config Files
+
+### phpcs.xml (existing, unchanged)
+
+PSR-12 with two exclusions:
+
+- `PSR12.Operators.OperatorSpacing`
+- `PSR1.Files.SideEffects.FoundWithSymbols`
+
+### captainhook.json (new)
+
+Pre-commit hook that runs `phpcbf` on staged PHP files.
+If unfixable issues remain, the commit is blocked.
+
+## Pre-commit Hook Behavior
+
+1. Developer commits
+2. CaptainHook triggers pre-commit
+3. Runs `phpcbf` on staged PHP files
+4. If all issues auto-fixed, commit proceeds
+5. If unfixable issues remain, commit blocked
+
+## What Does NOT Change
+
+- `phpcs.xml` rules stay the same
+- CI workflow unchanged (Codacy handles remote checks)
+- No functional code changes
diff --git a/docs/plans/2026-02-23-phpcs-captainhook-plan.md b/docs/plans/2026-02-23-phpcs-captainhook-plan.md
new file mode 100644
index 0000000..597dd8a
--- /dev/null
+++ b/docs/plans/2026-02-23-phpcs-captainhook-plan.md
@@ -0,0 +1,340 @@
+# PHPCS + CaptainHook Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use
+> superpowers:executing-plans to implement this plan
+> task-by-task.
+
+**Goal:** Add PHPCS/PHPCBF as a dev dependency with
+composer scripts and CaptainHook-managed pre-commit
+hook for automatic formatting.
+
+**Architecture:** Install three dev packages
+(`squizlabs/php_codesniffer`, `captainhook/captainhook`,
+`captainhook/hook-installer`). Add `composer lint` and
+`composer format` scripts. Configure CaptainHook to run
+PHPCBF on staged PHP files before each commit. The
+existing `phpcs.xml` is unchanged.
+
+**Tech Stack:** PHP 8.4, Composer, PHPCS/PHPCBF,
+CaptainHook
+
+---
+
+## Task 1: Install squizlabs/php\_codesniffer
+
+**Files:**
+
+- Modify: `composer.json` (auto-updated by composer)
+- Modify: `composer.lock` (auto-updated by composer)
+
+### Step 1: Install the package
+
+Run:
+
+```bash
+composer require --dev squizlabs/php_codesniffer
+```
+
+Expected: Package installs successfully.
+`composer.json` now lists `squizlabs/php_codesniffer`
+in `require-dev`.
+
+### Step 2: Verify phpcs works with existing config
+
+Run:
+
+```bash
+vendor/bin/phpcs --standard=phpcs.xml app/ tests/
+```
+
+Expected: Either clean output or a list of violations.
+The command should not error out — it should find and
+use `phpcs.xml`.
+
+### Step 3: Verify phpcbf works
+
+Run:
+
+```bash
+vendor/bin/phpcbf --standard=phpcs.xml app/ tests/
+```
+
+Note: `phpcbf` exits non-zero when it finds fixable
+issues. The `|| true` prevents that from stopping
+execution. What matters is it runs without crashing.
+
+### Step 4: Commit
+
+```bash
+git add composer.json composer.lock
+git commit -m "build(deps): add squizlabs/php_codesniffer"
+```
+
+---
+
+## Task 2: Add composer lint and format scripts
+
+**Files:**
+
+- Modify: `composer.json` — add two entries to
+ `"scripts"`
+
+### Step 1: Add the scripts
+
+In `composer.json`, add these two entries inside the
+`"scripts"` object:
+
+```json
+"lint": "vendor/bin/phpcs",
+"format": "vendor/bin/phpcbf || true"
+```
+
+Note: `phpcbf` returns exit code 1 when it fixes files
+(which is normal behavior, not an error). The `|| true`
+prevents composer from treating successful fixes as
+failures. If there are unfixable errors, `phpcbf`
+returns exit code 2, but `|| true` masks that too —
+this is acceptable since `composer lint` is the proper
+check command.
+
+### Step 2: Verify `composer lint` works
+
+Run:
+
+```bash
+composer lint
+```
+
+Expected: Runs `phpcs` against the project. Either
+reports violations or shows no output (clean).
+
+### Step 3: Verify `composer format` works
+
+Run:
+
+```bash
+composer format
+```
+
+Expected: Runs `phpcbf`. Auto-fixes any fixable
+violations.
+
+### Step 4: Run tests to confirm nothing broke
+
+Run:
+
+```bash
+composer test
+```
+
+Expected: All 14 tests pass.
+
+### Step 5: Commit
+
+```bash
+git add composer.json
+git commit -m "build: add composer lint and format scripts"
+```
+
+---
+
+## Task 3: Install CaptainHook
+
+**Files:**
+
+- Modify: `composer.json` (auto-updated by composer)
+- Modify: `composer.lock` (auto-updated by composer)
+
+**Step 1: Install captainhook and the hook-installer
+plugin**
+
+Run:
+
+```bash
+composer require --dev captainhook/captainhook captainhook/hook-installer
+```
+
+Note: The installer may prompt about allowing the
+plugin. Answer yes. If `composer.json`'s
+`config.allow-plugins` needs updating, composer will
+do it automatically when you approve.
+
+### Step 2: Verify CaptainHook is available
+
+Run:
+
+```bash
+vendor/bin/captainhook --version
+```
+
+Expected: Prints a version string
+(e.g. `CaptainHook x.x.x`).
+
+### Step 3: Commit
+
+```bash
+git add composer.json composer.lock
+git commit -m "build(deps): add captainhook and hook-installer"
+```
+
+---
+
+## Task 4: Configure CaptainHook pre-commit hook
+
+**Files:**
+
+- Create: `captainhook.json`
+
+### Step 1: Create the CaptainHook config
+
+Create `captainhook.json` in the project root with
+this content:
+
+```json
+{
+ "pre-commit": {
+ "enabled": true,
+ "actions": [
+ {
+ "action": "vendor/bin/phpcbf --standard=phpcs.xml {$STAGED_FILES|of-type:php}",
+ "config": {
+ "label": "Fix code style with PHPCBF"
+ }
+ },
+ {
+ "action": "vendor/bin/phpcs --standard=phpcs.xml {$STAGED_FILES|of-type:php}",
+ "config": {
+ "label": "Check code style with PHPCS"
+ }
+ }
+ ]
+ },
+ "pre-push": {
+ "enabled": false,
+ "actions": []
+ },
+ "commit-msg": {
+ "enabled": false,
+ "actions": []
+ }
+}
+```
+
+The two pre-commit actions run in order:
+
+1. `phpcbf` auto-fixes what it can
+2. `phpcs` checks for remaining violations — if any
+ exist, the commit is blocked
+
+### Step 2: Install the hooks into `.git/hooks`
+
+Run:
+
+```bash
+vendor/bin/captainhook install --force
+```
+
+Expected: CaptainHook installs hook scripts into
+`.git/hooks/`. Output mentions installing hooks.
+
+### Step 3: Verify the hook is installed
+
+Run:
+
+```bash
+head -5 .git/hooks/pre-commit
+```
+
+Expected: Shows a CaptainHook-generated script
+(not a sample hook).
+
+### Step 4: Commit
+
+```bash
+git add captainhook.json
+git commit -m "build: configure CaptainHook pre-commit hook for PHPCS"
+```
+
+Note: This commit itself will trigger the pre-commit
+hook for the first time. If it blocks due to formatting
+issues in existing files, run `composer format` first,
+then re-stage and commit.
+
+---
+
+## Task 5: Fix existing violations and final verify
+
+### Step 1: Run the formatter on the whole project
+
+Run:
+
+```bash
+composer format
+```
+
+Expected: Fixes any existing violations across `app/`
+and `tests/`.
+
+### Step 2: Run the linter to confirm clean
+
+Run:
+
+```bash
+composer lint
+```
+
+Expected: No violations reported (clean exit).
+
+### Step 3: Run tests to confirm nothing broke
+
+Run:
+
+```bash
+composer test
+```
+
+Expected: All 14 tests pass.
+
+### Step 4: Commit any formatting changes
+
+```bash
+git add -A
+git status
+git commit -m "style: auto-fix code style with phpcbf"
+```
+
+Note: Only commit if there are actual changes. If
+`composer format` made no changes, skip this step.
+
+---
+
+## Task 6: Update CLAUDE.md
+
+**Files:**
+
+- Modify: `CLAUDE.md` — add `composer lint` and
+ `composer format` to Commands section, note
+ CaptainHook in Code Standards
+
+### Step 1: Update CLAUDE.md
+
+In the `## Commands` section, add:
+
+```markdown
+- `composer lint` — Check code style (PHPCS)
+- `composer format` — Auto-fix code style (PHPCBF)
+```
+
+In the `## Code Standards` section, add a note:
+
+```markdown
+- CaptainHook pre-commit hook runs PHPCBF then
+ PHPCS on staged PHP files automatically
+```
+
+### Step 2: Commit
+
+```bash
+git add CLAUDE.md
+git commit -m "docs: add lint/format commands and hook info to CLAUDE.md"
+```
diff --git a/phpcs.xml b/phpcs.xml
index 2260283..27ecceb 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,6 +1,8 @@
PHP_CodeSniffer configuration
+ app
+ tests
diff --git a/phpmd.xml b/phpmd.xml
new file mode 100644
index 0000000..9899e36
--- /dev/null
+++ b/phpmd.xml
@@ -0,0 +1,24 @@
+
+
+ PHPMD rules for Branch Usage Checker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Feature/Commands/CheckCommandTest.php b/tests/Feature/Commands/CheckCommandTest.php
index aa08b0e..2ad60ba 100644
--- a/tests/Feature/Commands/CheckCommandTest.php
+++ b/tests/Feature/Commands/CheckCommandTest.php
@@ -1,7 +1,165 @@
artisan('check ivuorinen branch-usage-checker')
- // ->expectsOutput('')
- ->assertExitCode(0);
+use Illuminate\Support\Facades\Http;
+
+const TEST_VENDOR = 'test-vendor';
+const TEST_PACKAGE = 'test-package';
+const TEST_COMMAND = 'check ' . TEST_VENDOR . ' ' . TEST_PACKAGE;
+const TEST_METADATA_URL = 'packagist.org/packages/' . TEST_VENDOR . '/' . TEST_PACKAGE . '.json';
+const TEST_STATS_URL = 'packagist.org/packages/' . TEST_VENDOR . '/' . TEST_PACKAGE . '/stats';
+
+beforeEach(function () {
+ Http::preventStrayRequests();
+});
+
+function validMetadata(): array
+{
+ return [
+ 'package' => [
+ 'name' => TEST_VENDOR . '/' . TEST_PACKAGE,
+ 'description' => 'Test',
+ 'time' => '2024-01-01T00:00:00+00:00',
+ 'type' => 'library',
+ 'repository' => 'https://github.com/' . TEST_VENDOR . '/' . TEST_PACKAGE,
+ 'language' => 'PHP',
+ 'versions' => [
+ 'dev-main' => ['version' => 'dev-main'],
+ 'dev-feature' => ['version' => 'dev-feature'],
+ '1.0.0' => ['version' => '1.0.0'],
+ ],
+ ],
+ ];
+}
+
+function statsResponse(array $downloads): array
+{
+ return [
+ 'labels' => ['2024-01', '2024-02', '2024-03'],
+ 'values' => [$downloads],
+ ];
+}
+
+function fakePackageResponses(array $statsPerBranch = []): void
+{
+ $fakes = [TEST_METADATA_URL => Http::response(validMetadata())];
+ foreach ($statsPerBranch as $branch => $response) {
+ $fakes[TEST_STATS_URL . '/' . $branch . '.json*'] = $response;
+ }
+ Http::fake($fakes);
+}
+
+test('check command with slash format', function () {
+ fakePackageResponses([
+ 'dev-feature' => Http::response(statsResponse([1, 2, 3])),
+ 'dev-main' => Http::response(statsResponse([1, 2, 3])),
+ ]);
+
+ $this->artisan('check ' . TEST_VENDOR . '/' . TEST_PACKAGE)
+ ->assertExitCode(0);
+});
+
+test('check command with two arguments', function () {
+ fakePackageResponses([
+ 'dev-feature' => Http::response(statsResponse([1, 2, 3])),
+ 'dev-main' => Http::response(statsResponse([1, 2, 3])),
+ ]);
+
+ $this->artisan('check ' . TEST_VENDOR . ' ' . TEST_PACKAGE)
+ ->assertExitCode(0);
+});
+
+test('check command rejects invalid input', function (string $args, string $expected) {
+ $this->artisan($args)
+ ->expectsOutputToContain($expected)
+ ->assertExitCode(1);
+})->with([
+ 'missing package' => ['check ivuorinen', 'Missing package name'],
+ 'conflicting arguments' => ['check ivuorinen/branch-usage-checker extra', 'Conflicting arguments'],
+ 'invalid vendor' => ['check INVALID!/package-name', 'Invalid vendor name'],
+ 'invalid package' => ['check valid-vendor INVALID!', 'Invalid package name'],
+]);
+
+test('check command with 404 shows package not found', function () {
+ Http::fake([
+ 'packagist.org/packages/test-vendor/nonexistent-pkg.json' => Http::response([], 404),
+ ]);
+
+ $this->artisan('check test-vendor nonexistent-pkg')
+ ->expectsOutputToContain('Package not found')
+ ->assertExitCode(1);
+});
+
+test('check command with 500 shows server error', function () {
+ Http::fake([
+ TEST_METADATA_URL => Http::response([], 500),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('Failed to fetch package metadata (HTTP 500)')
+ ->assertExitCode(1);
+});
+
+test('check command skips branch when stats fetch fails', function () {
+ fakePackageResponses([
+ 'dev-feature' => Http::response([], 500),
+ 'dev-main' => Http::response(statsResponse([10, 20, 30])),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('Failed to fetch stats for dev-feature')
+ ->assertExitCode(0);
+});
+
+test('check command skips branch on connection failure', function () {
+ fakePackageResponses([
+ 'dev-feature' => Http::failedConnection(),
+ 'dev-main' => Http::response(statsResponse([10, 20, 30])),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('Failed to fetch stats for dev-feature')
+ ->assertExitCode(0);
+});
+
+test('check command stops when all stats fail', function () {
+ fakePackageResponses([
+ 'dev-feature' => Http::response([], 500),
+ 'dev-main' => Http::response([], 500),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('No statistics found... Stopping.')
+ ->assertExitCode(0);
+});
+
+test('check command lets TypeError propagate from malformed payload', function () {
+ Http::fake([
+ TEST_METADATA_URL => Http::response([
+ 'package' => ['versions' => 'not-an-array'],
+ ]),
+ ]);
+
+ $this->artisan(TEST_COMMAND);
+})->throws(\TypeError::class);
+
+test('check command shows no suggestions when all branches have downloads', function () {
+ fakePackageResponses([
+ 'dev-main' => Http::response(statsResponse([10, 20, 30])),
+ 'dev-feature' => Http::response(statsResponse([5, 10, 15])),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('No suggestions available. Good job!')
+ ->assertExitCode(0);
+});
+
+test('check command suggests branches with zero downloads', function () {
+ fakePackageResponses([
+ 'dev-main' => Http::response(statsResponse([10, 20, 30])),
+ 'dev-feature' => Http::response(statsResponse([0, 0, 0])),
+ ]);
+
+ $this->artisan(TEST_COMMAND)
+ ->expectsOutputToContain('Found 1 branches')
+ ->assertExitCode(0);
});