Skip to content

Releases: SanderMuller/project-boost-laravel

v1.3.0

Choose a tag to compare

@SanderMuller SanderMuller released this 15 Aug 15:20
Immutable release. Only release title and notes can be modified.
f7555f9

project-boost:sync now retires laravel/boost's boost.json once it has taken over what that file describes — the step that stops herd link from silently re-seeding guidance behind this package. Additive; no migration.

Action required: this release requires sandermuller/boost-core ^1.6 (up from ^1.0).

composer require --dev "sandermuller/project-boost-laravel:^1.3" -W

Added

  • project-boost:sync retires laravel/boost's boost.json after a successful sync. The file is laravel/boost's install state, read by boost:install and boost:update and nothing else — not the MCP server (boost:mcp starts a server the ServiceProvider registers unconditionally), not this package (it re-derives from vendor/laravel/boost/.ai/ every run), not boost-core. Retiring it is what stops the automatic re-seed: boost:update bails out when the file is missing, and herd link runs php artisan boost:update on its own whenever vendor/laravel/boost is present (Herd's bundled valet CLI), which otherwise rewrites the guidance files inside laravel/boost's marker and reinstalls its skill directories behind this command's back.
    • Adopt before retire. The file's agents list is the only record of what was picked in laravel/boost's installer, and nothing imports it automatically. While it names an agent the project's own config does not declare, the file stays put and the sync says which agent and how to adopt it — vendor/bin/boost install pre-selects exactly that set. An agent boost-core has no case for (antigravity, factory, grok_build, pi, zed) can never be adopted, so it never blocks; the sync names it instead, because nothing this package emits reaches that agent and retiring the file ends laravel/boost's updates for it too.
    • Archived, not deleted. The file moves to .boost/boost.json.retired (or .config/boost/boost.json.retired when the project uses that config layout — the layout decides, not whichever directory happens to exist). Both directories are gitignored by boost-core and skipped by its stale-file sweep, so the archive survives later syncs and never dirties the working tree. An existing archive is never overwritten: identical content means the source is simply dropped, and different content is archived alongside under a content-addressed name. Restore from there, or run php artisan boost:install to regenerate.
    • Only on a real takeover. A sync that injected no laravel/boost skills or guidelines (laravel/boost export-ignores its .ai payload, so a prefer-dist install has none), or that skipped a guidance path because it is a live symlink, has taken over nothing — neither case is an error, so the sync still exits 0, and in both the file is kept with the reason printed.
    • Further guards: with gitignore management off there is no state directory to archive into, so the file is kept rather than parked somewhere untracked; a symlink anywhere on the destination path is refused (rename() would follow it out of the project); an archive name already taken by different content is refused rather than overwritten; a boost.json recording no agent list is not laravel/boost's live install state — another tool's file, or one boost:update already refuses to act on — and is kept; a failed sync keeps the file, since laravel/boost's own path stays the fallback; a failed archive leaves the original in place.
    • --dry-run reports would-archive and moves nothing. --keep-boost-json opts out entirely.

Changed

  • Requires sandermuller/boost-core ^1.6 (was ^1.0). Two behaviours the retirement flow is documented against landed in 1.6.0: the stale-file sweep is manifest-gated, so the .boost/boost.json.retired archive survives later syncs instead of being reaped as unowned; and boost install pre-selects the agents recorded in laravel/boost's boost.json, which is the adoption step this command waits for before retiring the file.

--keep-boost-json joins the frozen CLI surface in PUBLIC_API.md from this release. Validated across the CI matrix (PHP 8.3/8.4 × Laravel 12/13, prefer-lowest and prefer-stable).

Full Changelog: 1.2.0...1.3.0

v1.2.0

Choose a tag to compare

@SanderMuller SanderMuller released this 05 Aug 22:07
Immutable release. Only release title and notes can be modified.

Restores compatibility with laravel/roster 1.0.0, whose API rewrite made every composer update on a consumer app hard-crash during the project-boost:sync post-hook.

Action required: this release requires laravel/boost ^2.5 (up from ^2.4). See UPGRADING.md.

composer require --dev "sandermuller/project-boost-laravel:^1.2" -W

-W matters: laravel/boost is usually a sibling top-level require in the consuming app, so it has to move to ^2.5 in the same resolve.

Fixed

  • composer update / install no longer fatals with Class "Laravel\Roster\Enums\Packages" not found. laravel/roster 1.0.0 (2026-07-18) removed the Packages enum and the Roster class this package was built against. The failure was unrecoverable rather than degrading to the intended permissive fallback: LaravelBoostGuidelineGate::EXCLUDED_PACKAGES referenced enum cases in a class-constant initializer, which PHP evaluates on class initialization — so permissive(), the graceful-fallback path itself, threw before any class_exists() guard could run. Package identity is now a plain composer/npm name string throughout, and Roster::scan() becomes ProjectScan::scan().

  • Guideline dirs resolve through laravel/boost's own name mapper. Package name → guideline dir now delegates to PackageRegistry::guidelineName() instead of slugifying locally. Pre-1.0 Roster's Package::name() returned the enum case name (FLUXUI_PRO), which the old slugify handled correctly; Roster 1.0 returns the composer name (livewire/flux-pro), which it would not have. Left unfixed, this would have replaced the fatal with silence — every package guideline suppressed, no error.

  • npm-ecosystem packages are gated again. Discovery scans both ecosystems (php() + js()), matching laravel/boost's own DiscoverPackagePaths::packages(). The inertia-react, inertia-svelte, inertia-vue and tailwindcss guideline dirs are npm-driven and had no gate signal from a php-only scan.

Changed

  • Requires laravel/boost ^2.5 (was ^2.4). Laravel\Boost\Support\PackageRegistry — which this package now mirrors for package constants and the name → dir map — landed in 2.5.0 alongside boost's own Roster 1.0 adaptation. 2.4.x still requires laravel/roster ^0.5, so there is no version of this fix that works on the 2.4 line.

  • laravel/roster ^1.0 is now an explicit requirement. It was previously pulled in only transitively through laravel/boost, despite this package type-hinting its classes directly — which is how a major upstream rewrite reached consumers with no constraint to stop it.

  • The known-package universe is derived from the dirs laravel/boost ships under .ai/, rather than enumerated from a hardcoded list. Roster 1.0 removed the enum that supplied it, and boost's replacement keeps its name → dir map private. Scanning is also self-maintaining: a guideline dir boost adds in a future release is gated correctly without a release here. Verified equivalent against boost 2.5 — every package dir it ships was a Packages case, and the enum cases with no shipped dir were already no-ops.

Internal

  • LaravelBoostGuidelineGate::fromRoster() became fromProjectScan() and takes a ProjectScan; VersionResolver takes a ?ProjectScan. Both are @internal — the PUBLIC_API.md surface (CLI commands, options, exit codes, config keys) is unchanged.

  • Added a regression guard asserting the gate's exclusion and must-be-direct lists still match laravel/boost's own. The gate is a 1:1 mirror of DiscoverPackagePaths, and unlike a removed class, a changed policy array drifts silently — no fatal, just guidelines quietly emitted or suppressed.

  • Dropped the abandoned rector/type-perfect dev dependency, superseded by tomasvotruba/type-coverage, which now bundles it. Both installed made PHPStan abort during container compilation on a duplicate service registration, exiting non-zero with no output — so the quality gate looked green while analysing nothing.

Full Changelog: 1.1.0...1.2.0

v1.1.0

Choose a tag to compare

@SanderMuller SanderMuller released this 05 Jun 16:45
Immutable release. Only release title and notes can be modified.
cd13167

Adds project-boost:reconcile — a guided takeover that captures laravel/boost-seeded agent guidance before a sync would overwrite it. Additive; no migration.

Added

  • project-boost:reconcile — a diff-first guided takeover for the laravel/boost coexistence seam. boost:install seeds its guidelines directly into your agent files (CLAUDE.md / AGENTS.md / GEMINI.md / …) inside a <laravel-boost-guidelines> marker; a markerless boost-core sync regenerates those files wholesale, so hand-authored content outside the marker is lost. reconcile detects foreign-seeded files by that marker (the same signal vendor/bin/boost doctor uses), backs each up verbatim to .boost-reconcile/, captures the hand-authored residual into .ai/guidelines/reconciled.md (append-with-dedup — it never clobbers edits you have made there), then runs project-boost:sync so the captured content is re-composed into every agent file. Options: --dry-run, --force, --no-sync.
  • project-boost:sync now warns when a sync would overwrite laravel/boost-seeded guidance, pointing at project-boost:reconcile first (warn-and-continue, matching boost-core's default).
  • A post-boost:install nudge toward project-boost:reconcile, fired only after a bare boost:install that actually seeds guidance — silent after boost:install --mcp (which keeps laravel/boost's writers dormant, so nothing is seeded) and while project-boost:install is driving its own sequence.

Docs

  • New docs/laravel-coexistence.md: the canonical command sequence (boost:install once → project-boost:reconcile once → project-boost:sync ongoing), the division of labor with laravel/boost, and why a bare vendor/bin/boost sync on a wrapper project loses content. The README and PUBLIC_API.md (which adds project-boost:reconcile to the frozen CLI surface) link to it.

The new command is part of the 1.x @api/CLI surface from this release. Requires boost-core ^1.0; validated across the CI matrix.

Full Changelog: 1.0.0...1.1.0

v1.0.0

Choose a tag to compare

@SanderMuller SanderMuller released this 05 Jun 15:26
Immutable release. Only release title and notes can be modified.
6236f86

The stability commitment. project-boost-laravel joins the boost family's 1.0 line — the last freeze in dependency order (boost-core → package-boost-php → package-boost-laravel → here). No runtime change: 1.0.0 is the 0.10.2 surface, re-validated against the boost-core 1.x family and frozen.

The 1.0 contract

This package is a behavior wrapper, not a class library — its contract is the CLI, config, and sync behavior, documented in PUBLIC_API.md. From 1.0.0 that surface is locked for the 1.x line (no break in a MINOR or PATCH):

  • CLIproject-boost:install / :sync / :where, their documented options, and the exit-code contract.
  • Configconfig/project-boost-laravel.php keys (suppress_upstream_writers, laravel_boost_ai_root).
  • Discovery & frozen formats — the service-provider FQCN, the BoostWrapper contract, the laravel/boost tag manifest, and the install-gated guideline behavior.
  • No @api PHP classes — nothing here is constructed or extended by name; the SkillRenderer implementation stays @internal.

Changed

  • Requires sandermuller/boost-core ^1.0 (was ^0.23.0||^1.0) — the 0.23 range is dropped. boost-core 1.0 is a drop-in over 0.23.3 (no API break), so a consumer already on ^0.23 bumps mechanically. If you require sandermuller/boost-core or sandermuller/boost-skills directly, move them to the 1.0 family in the same composer update -W (boost-skills 2.2.0 already admits boost-core ^1.0). See UPGRADING.md.

Re-validated against the resolved boost-core 1.x family across the CI matrix (prefer-lowest → 1.0.0, prefer-stable → 1.1.0): full suite green, the boost-core @api-closure guard intact, static analysis clean, sync no-drift.

Full Changelog: 0.10.2...1.0.0

v0.10.2

Choose a tag to compare

@SanderMuller SanderMuller released this 05 Jun 11:13
Immutable release. Only release title and notes can be modified.
2f7225c

Changed

  • Now allows sandermuller/boost-core ^1.0 (was ^0.23.0). boost-core 1.0.0 is a drop-in over 0.23.3 (no API break), so the runtime requirement widens to ^0.23.0||^1.0. This unblocks wrapper consumers from resolving boost-core 1.0 transitively — project-boost-laravel was the last consumer-facing cap in the cascade (boost-skills already admits ^1.0). Validated against the 0.23.3 surface, which is byte-identical to 1.0.0: full suite green (65/65), the boost-core @api-closure guard intact, PHPStan clean.

    Consumers that require sandermuller/boost-core directly can bump it to ^1.0 in the same composer update -W. No action needed otherwise — a scoped update under an existing ^0.10 constraint picks this up.

Full Changelog: 0.10.1...0.10.2

v0.10.1

Choose a tag to compare

@SanderMuller SanderMuller released this 04 Jun 19:32
Immutable release. Only release title and notes can be modified.
9438a09

Fixed

  • project-boost:where now install-gates guidelines exactly like project-boost:sync. where built its guideline reader WITHOUT the install-gate sync applies, so it over-reported laravel/boost guidelines (inertia / livewire / pest / sail …) for packages the host hasn't installed — misleading for a command whose whole job is "what ships for this project shape." Both commands now share the gate through a single GatesGuidelines concern, so where reports the set sync actually emits.
  • The laravel_boost_ai_root config override is now honored. The key shipped in config/project-boost-laravel.php — documented as a test / non-standard-vendor-layout override — was never read; both commands hard-coded vendor/laravel/boost/.ai. project-boost:sync and project-boost:where now resolve the asset root through it (falling back to the standard vendor path), making the documented override actually work.

Docs

  • UPGRADING.md: corrected the 0.10 upgrade command, sourced from consumer adoption feedback. The prior command was incomplete on three counts: it omitted --dev (a bare composer require silently promotes this dev package into production require); consumers that require sandermuller/boost-skills directly must co-bump it to ^2.1 -W in the same command (a directly-required boost-skills ^2.0.6 transitively pins boost-core ^0.22, colliding with 0.10's ^0.23 floor, and -W alone can't resolve a sibling top-level require); and each boost package should be pinned to the floor you validated against, not the wrapper's transitive floor, to avoid generated-guidance drift in repos that auto-run project-boost:sync from composer scripts.

Internal

  • The project-boost:where guideline-gate regression test is now hermetic: it points the laravel_boost_ai_root override at a small fixture .ai tree instead of the real vendor/laravel/boost/.ai payload — which laravel/boost export-ignores, so it is absent from a prefer-dist Composer install (and was failing the CI test matrix while passing locally).
  • Removed the orphaned VersionResolver::withHostRoster() static factory, left dead by the where gate refactor.

Full Changelog: 0.10.0...0.10.1

v0.10.0

Choose a tag to compare

@SanderMuller SanderMuller released this 04 Jun 15:37
Immutable release. Only release title and notes can be modified.
64d6900

Adopts the boost-core 0.23 line and locks the package's @api/@internal surface ahead of 1.0.

Breaking

  • Requires sandermuller/boost-core ^0.23 (was ^0.22), alongside sandermuller/package-boost-laravel ^0.15 and sandermuller/boost-skills ^2.1. Most apps only bump this package — boost-core resolves transitively:

    composer require "sandermuller/project-boost-laravel:^0.10"

    If you require any boost package directly, move them to the 0.23 line together. Running against boost-core < 0.23 no longer resolves.

Fixed

  • A malformed metadata.boost-tags now fails closed. laravel/boost skills are tag-filtered against your withTags(). Previously a malformed (non-string) boost-tags value was treated as "untagged" and shipped to every agent — the opposite of the engine's fail-closed contract. The package now tokenizes and validates tags through boost-core's canonical BoostTags, so a malformed value ships nowhere, matching boost's own behaviour. (An explicitly-empty boost-tags is untagged on purpose — no sidecar-manifest fallback.)

Internal

  • Locked the @api/@internal surface for 1.0, documented in the new PUBLIC_API.md. This package exposes no @api PHP classes — it's an artisan/CLI-driven wrapper, so its semver-protected contract is its CLI commands, config keys, and documented behaviour, not a class API. An architecture test fails if any src/ class is added without an explicit @api/@internal mark.

Full Changelog: 0.9.1...0.10.0

v0.9.1

Choose a tag to compare

@SanderMuller SanderMuller released this 04 Jun 06:46
Immutable release. Only release title and notes can be modified.
4404f5c

A patch hardening the upgrade path, from real-world adoption feedback across several consumer apps.

Fixed

  • A pre-0.20 variadic withTags(...) in your boost config no longer aborts composer update. boost-core 0.20 made withTags(Tag::Php, ...) into withTags([...]), and the project-boost:sync composer hook requires your config — so an un-migrated variadic call threw a TypeError that aborted the whole update with a raw stack trace. project-boost:sync, project-boost:where, and project-boost:install now catch a config-load failure and print a clean, actionable migration hint with a non-zero exit instead. (A missing config likewise gets a friendly "create one" hint rather than an uncaught exception.)
  • The README minimal example used the pre-0.20 variadic withTags(Tag::Laravel, Tag::Php) — which would TypeError if copy-pasted under boost-core ^0.22. Corrected to the array form.

Internal

  • Added an @api-closure conformance test: it scans every boost-core import under src/ and asserts each symbol is part of boost-core's frozen @api surface, so the package can only depend on the 1.0-stable contract.

Docs

  • UPGRADING.md: hand-edit withTags(...)withTags([...]) before bumping (the post-update sync hook loads your config before any auto-migration can run).
  • README.md: clarified that boost-core config resolves from .config/boost.php or a legacy root boost.php, while laravel/boost's boost.json stays at the project root (laravel/boost owns it).

Full Changelog: 0.9.0...0.9.1

v0.9.0

Choose a tag to compare

@SanderMuller SanderMuller released this 03 Jun 20:51
Immutable release. Only release title and notes can be modified.
1812278

Adopts the boost-core 0.22 line and moves every sync-driving and wrapper code path onto boost-core's frozen @api surface, ahead of boost-core's 1.0 freeze. Also fixes the console commands for the canonical .config/boost.php config layout.

Breaking

  • Requires boost-core ^0.22 (was ^0.16). The boost stack moves together — sandermuller/package-boost-laravel ^0.14 (the Laravel umbrella that ships the emit(): iterable FileEmitter contract and floors package-boost-php ^0.18.1) and sandermuller/boost-skills ^2.0.6. Running against boost-core < 0.22 no longer resolves.

    Most apps only need to bump this package — boost-core comes through transitively:

    composer require "sandermuller/project-boost-laravel:^0.9"

    If you pin any boost package directly, move them to the 0.22 line together: sandermuller/boost-core ^0.22, sandermuller/package-boost-laravel ^0.14, sandermuller/boost-skills ^2.0.6.

Fixed

  • project-boost:sync, project-boost:sync --dry-run, project-boost:where, and the non-interactive project-boost:install now resolve the boost config from both the legacy root boost.php and the canonical .config/boost.php layout (boost-core ≥ 0.17). They previously hard-coded a root boost.php check and aborted with No boost.php found for projects already on the .config/ layout, before the sync ever ran.

Internal

  • Re-pointed the package onto boost-core's @api surface: sync now drives the BoostSync facade, agent targets resolve via Agent::target(), config reads via BoostConfig::load(), skill emit paths via AgentTarget::skillRelativePathForName(), and frontmatter via the now-@api FrontmatterParser. Every boost-core symbol the package imports is @api, so it stays stable under boost-core's 1.0 semver guarantee — no longer relying on engine internals.

Full Changelog: 0.8.2...0.9.0

v0.8.2

Choose a tag to compare

@SanderMuller SanderMuller released this 31 May 16:26
Immutable release. Only release title and notes can be modified.
a2dd0a2

0.8.2

Makes guideline and skill discovery order deterministic across operating systems, eliminating content-free sync churn between macOS and Linux. Patch release — no constraint or API changes.

Fixed

Deterministic guideline/skill ordering across OSes

LaravelBoostGuidelineReader and LaravelBoostAssetReader walked vendor/laravel/boost/.ai/ with a Symfony Finder that had no sortByName(), so each yielded entries in filesystem-iteration order — APFS hash order on macOS, ext4 readdir order on Linux. The guideline reader's output is appended in that order and boost-core's SyncEngine concatenates guidelines into CLAUDE.md / AGENTS.md / GEMINI.md in array order.

The result: the same commit regenerated those agent files with a different section order depending on which OS ran the sync. Downstream that showed up as a large, content-free reorder diff (~157 lines, zero content change) and — worse — a CI auto-fix loop, where CI on Linux kept rewriting the order a developer had committed on macOS and pushing the "fix" back.

Both finders now call ->sortByName(), pinning a stable lexicographic order regardless of the underlying filesystem. A reader test asserts the native emission order is already lexicographic (it fails without the sort).

If you previously worked around this by gating sync off in CI, you can drop that guard once on 0.8.2 — with deterministic ordering, regenerating in CI is byte-stable against a local sync of the same commit.

Full Changelog: 0.8.1...0.8.2