sandermuller / package-boost
AI-assisted development tooling for Composer packages — Laravel-aware and framework-agnostic
Requires
- php: ^8.2
- illuminate/console: ^11.0||^12.0||^13.0
- illuminate/support: ^11.0||^12.0||^13.0
Requires (Dev)
- driftingly/rector-laravel: ^2.2
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- orchestra/testbench: ^9.0||^10.0||^11.0
- pestphp/pest: ^3.0||^4.0
- pestphp/pest-plugin-arch: ^3.0||^4.0
- pestphp/pest-plugin-laravel: ^3.0||^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- rector/rector: ^2.0
- rector/type-perfect: ^2.1
- spaze/phpstan-disallowed-calls: ^4.6
- stolt/lean-package-validator: ^5.7.1
- symplify/phpstan-extensions: ^12.0
- tomasvotruba/cognitive-complexity: ^1.0
- tomasvotruba/type-coverage: ^2.1
README
AI tooling sync for Composer packages — Laravel-aware, framework-agnostic supported. Bridges the gap between Laravel Boost (designed for applications) and package development with Orchestra Testbench, and works the same way for non-Laravel Composer packages that adopt Testbench as a dev-only harness.
What It Does
- Syncs
.ai/skills/to per-agent skill dirs (.claude/skills/,.cursor/skills/,.agents/skills/,.github/skills/,.junie/skills/,.kiro/skills/) so 9 agents — Claude Code, Cursor, GitHub Copilot, Codex, Gemini, Junie, Kiro, OpenCode, Amp — can use them - Syncs
.ai/guidelines/intoCLAUDE.md,AGENTS.md, andGEMINI.md - Generates
.mcp.jsonpointing tovendor/bin/testbench boost:mcpwhen Boost is installed - Ships a
package-developmentskill that teaches AI agents how to work with Testbench - Ships a
cross-version-laravel-supportskill for packages that must span multiple Laravel / PHP majors — composer constraints, feature detection, version guards, Testbench compatibility - Ships a
ci-matrix-troubleshootingskill that diagnoses red CI matrix cells —prefer-lowest/prefer-stableresolves, security-advisories floors, Testbench / PHPUnit version interlock - Ships a
lean-distskill that on-ramps consumers tostolt/lean-package-validatorfor.gitattributeshygiene, with AI-eraexport-ignoreentries (.ai,.claude,AGENTS.md,CLAUDE.md, …) lpv's defaults don't cover - Ships a
readmeskill that teaches the two README shapes (stub vs comprehensive), required sections, voice, and a canonical staleness-audit pattern — with areferences/laravel-package.mdlayer for Laravel-package-specific conventions - Ships a
release-notesskill that helps maintainers draft GitHub release bodies — defaulting to GitHub's auto-generated format and overriding only when major/breaking — with Laravel-package-specific guidance for version-matrix shifts and CHANGELOG-automation interplay - Ships an
upgradingskill that teaches the canonical UPGRADING.md shape (reverse-chronological per-major sections, version-comment-labelled before/after code, impact tagging, stable H2 anchors that release-notes'## Breaking changesbullets link to) — with Laravel-package conventions for the four observed filename variants and ecosystem-plugin upgrade patterns - Ships a
skill-authoringskill that guards against silent skill-name collisions across host / vendor / package-boost defaults, teaches activating frontmatter, and pins the.ai/skills/vsresources/boost/skills/source-dir choice
Agent coverage
package-boost:sync writes to the same paths each agent reads from, mirroring Laravel Boost's src/Install/Agents/ matrix:
| Agent | Guidelines file | Skills dir |
|---|---|---|
| Claude Code | CLAUDE.md |
.claude/skills |
| Cursor | AGENTS.md |
.cursor/skills |
| GitHub Copilot | AGENTS.md |
.github/skills |
| Codex CLI | AGENTS.md |
.agents/skills |
| Gemini CLI | GEMINI.md |
.agents/skills |
| Junie | AGENTS.md |
.junie/skills |
| Kiro | AGENTS.md |
.kiro/skills |
| OpenCode | AGENTS.md |
.agents/skills |
| Amp | AGENTS.md |
.agents/skills |
.agents/skills is shared across Codex, Gemini, OpenCode, and Amp — sync writes there once and dedupes. Trim the list to the agents you actually use via package-boost:install (or set agents in config/package-boost.php directly).
Note on skill-dir consumption. Today only Claude Code natively reads its skill dir as auto-activatable skills (each
SKILL.mdbecomes a tool-callable agent). The other eight agents primarily consume the per-agent guidelines file (AGENTS.md/CLAUDE.md/GEMINI.md), which package-boost concatenates from.ai/guidelines/and shipped defaults. Their skill dirs are written for forward compatibility so the same.ai/skills/source becomes useful to each tool the moment it ships skill support — you don't need to re-author or re-sync.
Installation
composer require sandermuller/package-boost --dev
Add the service provider to your testbench.yaml:
providers: - SanderMuller\PackageBoost\PackageBoostServiceProvider
Framework-agnostic packages
Package-boost works for any Composer package, not just Laravel packages — Testbench is used as a dev-only command harness, not a runtime dependency of the package being developed. If your package doesn't already use Testbench, add it alongside package-boost:
composer require orchestra/testbench --dev composer require sandermuller/package-boost --dev
Create testbench.yaml at the package root with the framework
sentinel and the package-boost provider — nothing else is required:
laravel: '@testbench' providers: - SanderMuller\PackageBoost\PackageBoostServiceProvider
Then sync:
vendor/bin/testbench package-boost:sync
.mcp.json generation is skipped automatically when laravel/boost
isn't installed (mcp.action: "skipped", reason: "laravel-boost-not-installed" in the JSON output) — guidelines and
skills sync as normal.
The verified framework-agnostic flow is package-boost:sync plus the
zero-config "all agents" default. The interactive
package-boost:install picker described under Usage below
persists its choice via Testbench's workbench/ scaffolding helpers;
adopters that haven't run vendor/bin/testbench workbench:install
should either skip package-boost:install entirely (the default
already syncs every supported agent) or hand-edit
workbench/config/package-boost.php after a one-time
vendor/bin/testbench vendor:publish --tag=package-boost-config.
Some shipped skills are Laravel-specific by design
(package-development, cross-version-laravel-support,
ci-matrix-troubleshooting). They auto-activate only on prompts that
mention Laravel-shaped tooling and are otherwise idle, so they cost
nothing in a framework-agnostic repo.
Usage
1. (Optional) Pick which agents to sync
vendor/bin/testbench package-boost:install
Interactive picker — defaults to "all 9" and pre-checks any agent
already detected in your project (existing .cursor/, .kiro/, etc.)
or imported from laravel/boost's own boost.json if present. The
choice is persisted to workbench/config/package-boost.php.
Skip this step entirely to keep the zero-config default (all 9
agents). Override non-interactively with --all,
--agents=claude_code,cursor, or --no-import.
2. Create your skills and guidelines
.ai/
├── guidelines/
│ └── my-conventions.md
└── skills/
└── my-skill/
└── SKILL.md
Or scaffold them with the right frontmatter pre-filled:
vendor/bin/testbench package-boost:new skill my-skill --description="One-line auto-activation hook."
vendor/bin/testbench package-boost:new guideline my-conventions
package-boost:new rejects collisions unless you pass --force, and validates the name against the same kebab-case shape (^[a-z][a-z0-9-]*$) the frontmatter linter enforces — so a freshly scaffolded skill always passes package-boost:doctor without further edits.
3. Sync to agent directories
vendor/bin/testbench package-boost:sync
4. Commit the generated files
The sync copies your .ai/ files to the directories each AI tool expects. Commit both the source (.ai/) and the generated files (.claude/, .cursor/, .agents/, .github/, .junie/, .kiro/, CLAUDE.md, AGENTS.md, GEMINI.md).
Selective sync
vendor/bin/testbench package-boost:sync --skills vendor/bin/testbench package-boost:sync --guidelines vendor/bin/testbench package-boost:sync --mcp
CI drift check
vendor/bin/testbench package-boost:sync --check
Reports planned actions without writing. Exits non-zero if any skill, guideline, or MCP target differs from its source — or if any host .ai/skills/<name>/SKILL.md is missing required frontmatter (name, description) or has a name/directory mismatch. Shipped and vendor skill issues surface as warnings only; --check fails on host issues so CI catches them before they ship. Use in CI to catch "forgot to sync" commits.
JSON output
vendor/bin/testbench package-boost:sync --check --format=json
Emits a structured JSON document on stdout — parseable by jq or programmatic consumers:
{
"schema": 1,
"check": true,
"drift": false,
"skills": { "new": [], "updated": [], "removed": [], "unchanged": 6 },
"guidelines": { "new": [], "updated": [], "removed": [], "unchanged": 3 },
"mcp": { "action": "unchanged", "target": ".mcp.json" }
}
Shape contract:
skillsandguidelinescarry{ new, updated, removed, unchanged }. Each non-unchanged array holds per-target entries with fields:target(string) — always present, relative to the package root.hint(string, optional) — advisory prose. For skills:"symlink → <relative target>"onupdatedactions. For guidelines:"+N lines"/"-N lines"/"content updated"onupdated/newactions. No hint onremovedorunchanged. Not a command-to-run; the fix for any drift ispackage-boost:syncwithout--check.line_delta(int, optional, guidelines only) — integer line difference of the target file between its current state and what the sync would write. Only the<package-boost-guidelines>block is rewritten, soline_deltais effectively the synced-region delta (file content outside the block is never touched).
mcpcarries{ action, target }— always a single object, never an array.actionis"new","updated", or"unchanged".skippedcategories report structurally:skills/guidelineswhen no sources are found:{ "skipped": "no-sources" }.mcpwhen Laravel Boost isn't installed:{ "action": "skipped", "reason": "laravel-boost-not-installed" }.mcpwhenclaude_codeis filtered out of the agent selection:{ "action": "skipped", "reason": "claude-not-selected" }.
- Arrays are stable-sorted by
targetfor deterministic diffs across runs.
Example GitHub Actions step that fails the job and lists drifted targets:
- name: Check package-boost sync run: | report=$(vendor/bin/testbench package-boost:sync --check --format=json || true) drift=$(echo "$report" | jq -r '.drift') if [ "$drift" = "true" ]; then echo "::error::package-boost sync drift detected" echo "$report" | jq -r ' (.skills.new, .skills.updated, .skills.removed)[]?.target, (.guidelines.new, .guidelines.updated, .guidelines.removed)[]?.target, if .mcp.action == "new" or .mcp.action == "updated" then .mcp.target else empty end ' | sort -u | sed "s|^| - |" exit 1 fi
Pass --show-unchanged to turn the unchanged field from an int count into a full array of { target } entries.
Verbose output
vendor/bin/testbench package-boost:sync --show-unchanged
By default, the sync output lists only targets that changed and folds unchanged ones into the total: ... summary. Pass --show-unchanged to print a line per unchanged target as well.
Migrating from older versions
Prior to the multi-agent rollout, package-boost wrote
.github/copilot-instructions.md. Upstream Boost migrated Copilot
guidelines to AGENTS.md, so package-boost no longer writes that
file. Sync warns whenever the legacy file is detected with our tag
block. To remove it automatically — only when the file contains
nothing but a fresh-from-sync block:
vendor/bin/testbench package-boost:sync --prune
--prune refuses if the file has user content outside the block,
or if the block has been edited / is stale relative to current
.ai/ sources. Run a normal package-boost:sync first to refresh,
then --prune to clean up.
If you narrow package-boost.agents after a previous "all" sync,
sync also warns about leftover skill dirs / guideline files / mcp
entries from agents that fell out of the selection. To delete them
automatically, pass --prune-orphans:
vendor/bin/testbench package-boost:sync --prune-orphans
Skill dirs are removed wholesale (sync writes them in their
entirety). Guideline files have just the
<package-boost-guidelines> block stripped; the file is deleted
only when nothing but whitespace remains, so user-authored content
outside the block is preserved. .mcp.json has the laravel-boost
entry removed when claude_code is deselected.
One-shot diagnostic
vendor/bin/testbench package-boost:doctor
Aggregates the checks otherwise scattered across --check,
install, and the legacy / orphan warnings into a single report:
configured + effective agents, sync drift counts, SKILL.md
frontmatter issues, deselected-agent orphans, vendor skill
collisions, MCP / Boost detection, the legacy Copilot file, and the
.gitattributes managed block. Exits non-zero on any finding.
vendor/bin/testbench package-boost:doctor --format=json
JSON variant is stable-shaped (schema: 1) and parseable by jq.
.gitattributes hygiene
vendor/bin/testbench package-boost:lean
Idempotently writes a managed # >>> package-boost (managed) >>> /
# <<< package-boost (managed) <<< block into .gitattributes
covering AI-era export-ignore paths (.ai/, .claude/, .cursor/,
.agents/, .junie/, .kiro/, AGENTS.md, CLAUDE.md,
GEMINI.md, …) so composer archive / Packagist --prefer-dist
tarballs stay lean. User-authored entries outside the marker block
are preserved verbatim. Pass --check to fail CI when the block
drifts.
The shipped lean-dist skill teaches the validation side
(stolt/lean-package-validator); this command handles the write
side.
Composer script
{
"scripts": {
"sync-ai": "vendor/bin/testbench package-boost:sync"
}
}
Composer auto-sync hook
Register package-boost:sync as a post-autoload-dump hook so
composer install / update / dump-autoload catch "forgot to
sync" mistakes automatically. Complements the --check CI gate: CI
catches stale generated files on the server, the hook catches them
on the contributor's machine.
Strict variant (recommended) — matches the CI contract. If
anything has drifted, the install fails and the contributor re-runs
package-boost:sync by hand:
{
"scripts": {
"post-autoload-dump": [
"@php vendor/bin/testbench package-boost:sync --check"
]
}
}
Auto-fix variant — friendlier but mutates the working tree when
drift exists, which leaves uncommitted changes behind after a fresh
composer install on a dirty branch:
{
"scripts": {
"post-autoload-dump": [
"@php vendor/bin/testbench package-boost:sync"
]
}
}
Boost-less packages — if the host doesn't depend on
laravel/boost, narrow the hook to skip MCP (otherwise it emits a
"Laravel Boost is not installed" warn on every composer run):
{
"scripts": {
"post-autoload-dump": [
"@php vendor/bin/testbench package-boost:sync --check --skills --guidelines"
]
}
}
The hook runs via /bin/sh on posix and cmd.exe on Windows;
single-command-per-array-entry form works on both. Chained shell
operators (&&, ||) are not portable across composer's shell
layers — use separate array entries if you need multiple steps.
With Laravel Boost
When laravel/boost is also installed as a dev dependency, you get:
- MCP server —
package-boost:sync --mcpgenerates the correct.mcp.jsonconfig - Doc search — Boost's
search-docstool works out of the box via Testbench - Shipped
package-developmentskill — ships viaresources/boost/skills/and is bundled into every selected agent's skill dir bypackage-boost:sync, so downstream agents always get it regardless of Boost version. - Package-tuned foundation — ships
resources/boost/guidelines/foundation.mdwith package-dev framing (Testbench harness, semver, public API discipline).package-boost:syncbundles it into the<package-boost-guidelines>block ahead of any user-authored.ai/guidelines/content, separated by a horizontal rule. - App-only guidelines stripped — defaults exclude
foundation(Boost's app-tuned version), Inertia, Livewire, Filament, Volt, Folio, Pennant, Wayfinder, Nightwatch, Pulse, Herd, Sail, Tailwind, Vite, deployments, andlaravel/style|api|localization
Customising excluded guidelines
Publish the config and edit config/package-boost.php:
vendor/bin/testbench vendor:publish --tag=package-boost-config
The excluded_boost_guidelines array is merged into boost.guidelines.exclude at boot. Keys match Boost's GuidelineComposer keys exactly (e.g. livewire/core, filament/v4, herd).
Vendor-contributed skills and guidelines
Installed Composer packages that ship
resources/boost/skills/<name>/SKILL.md or
resources/boost/guidelines/*.md are picked up automatically and
merged into the sync. Load order:
- Package-boost's shipped defaults
- Vendor packages (alphabetical by
vendor/name) - Host
.ai/
For skills, later entries override earlier ones on name
collisions — host .ai/skills/<name> always wins over a vendor
contribution of the same name. For guidelines, each source
contributes its own block and they concatenate in load order,
separated by ---.
Disable discovery entirely or skip specific packages via
config/package-boost.php:
'discover_vendor_packages' => true, 'excluded_vendor_packages' => [ 'sandermuller/package-boost', ],
How It Differs from Laravel Boost
| Laravel Boost | Package Boost | |
|---|---|---|
| For | Laravel applications | Composer packages (Laravel-aware, framework-agnostic supported) |
| Runs via | php artisan |
vendor/bin/testbench |
| Discovers skills | From app + vendor packages | From .ai/ + vendor/*/resources/boost/ |
| Generates guidelines | Composes from installed packages | Copies .ai/ + merges vendor contributions |
| MCP server | Built-in | Delegates to Boost when installed |
Changelog
See CHANGELOG.md for a per-release history. Entries are auto-generated from GitHub Releases.
License
MIT