PHPackages                             sandermuller/boost-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. sandermuller/boost-core

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

sandermuller/boost-core
=======================

AI agent configuration sync for PHP projects. Author skills and guidelines once, publish to every agent.

0.17.0(1w ago)11.2k↓31.8%[1 PRs](https://github.com/SanderMuller/boost-core/pulls)7MITPHPPHP ^8.3CI passing

Since May 18Pushed 2d agoCompare

[ Source](https://github.com/SanderMuller/boost-core)[ Packagist](https://packagist.org/packages/sandermuller/boost-core)[ Docs](https://github.com/sandermuller/boost-core)[ RSS](/packages/sandermuller-boost-core/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (93)Versions (55)Used By (7)

boost-core
==========

[](#boost-core)

[![Latest Version on Packagist](https://camo.githubusercontent.com/2a84bd5caae5484e2ce0db00d8be4600e7382f06aa0369155252fabd998f652d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73616e6465726d756c6c65722f626f6f73742d636f72652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sandermuller/boost-core)[![Tests](https://camo.githubusercontent.com/ca24f4bc061a6bd5e0238c57985f014f9eada62aaea4665aabdb058b380e59bb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f73616e6465726d756c6c65722f626f6f73742d636f72652f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/sandermuller/boost-core/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/29ff787fb39c0c9826608f899461dcfc08c92e16708f646a4bfd339dfa4b8b36/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f73616e6465726d756c6c65722f626f6f73742d636f72652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sandermuller/boost-core)[![License](https://camo.githubusercontent.com/80467f0d5d05628d16bcf2075df94cb3f7e87270658a768e700c32c4de3ee44d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f73616e6465726d756c6c65722f626f6f73742d636f72652e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![Laravel Boost](https://camo.githubusercontent.com/90f2604136cf241bd1fa46f5c025c7dad12d8cdc1b66e7662c12003c7f79abf8/68747470733a2f2f62616467652e6c61726176656c2e636c6f75642f626f6f73742d62616467652e7376673f7374796c653d666c61742d737175617265)](https://github.com/laravel/boost)

> AI agent configuration sync for any PHP project. Write skills, guidelines, and commands once in `.ai/`; boost-core publishes them to nine agents: Claude Code, Cursor, Copilot, Codex, Gemini, Junie, Kiro, OpenCode, Amp. No framework dependency.

[![overview image](overview.png)](overview.png)

Contents
--------

[](#contents)

- [How sync works](#how-sync-works) · [Install](#install) · [Quickstart](#quickstart) · [What you get](#what-you-get)
- [Skill sources](#skill-sources) · [Tag filtering](#tag-filtering) · [Commands](#commands) · [Skill rendering](#skill-rendering)
- [Automating the sync](#automating-the-sync) · [Project Conventions](#project-conventions) · [File ownership](#file-ownership)
- [CLI reference](#cli-reference) · [Environment variables](#environment-variables) · [Versioning &amp; stability](#versioning--stability)

How sync works
--------------

[](#how-sync-works)

You author three kinds of content under `.ai/`, and `boost sync` fans each out to every agent you selected in `withAgents(...)`. One source, many agent-native copies:

You write inWhat it is`boost sync` fans it out to`.ai/skills/`Agent Skills (`/SKILL.md`)`.{agent}/skills//SKILL.md` per agent`.ai/guidelines/`Always-loaded guidance`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, Copilot file`.ai/commands/`Slash-command prompt templatesPer-agent command dirs (see [Commands](#commands))Skills and commands land in gitignored per-agent directories; the guidance files stay tracked. See [File ownership](#file-ownership) for why.

Install
-------

[](#install)

`boost-core` is the engine. You rarely install it directly. Instead you install the **family package** (a thin wrapper that bundles boost-core with a curated skill set) that matches what you're building, and it pulls `boost-core` in.

You're buildingInstallShipsA PHP application (not a package)[`sandermuller/project-boost`](https://github.com/sandermuller/project-boost)App-dev skills — DDD layering, repository pattern, DI, domain modeling, legacy coexistenceA Laravel application[`sandermuller/project-boost-laravel`](https://github.com/sandermuller/project-boost-laravel)`laravel/boost` MCP coexistence + nine-agent fanout + tag filter + remote skillsA framework-agnostic Composer package[`sandermuller/package-boost-php`](https://github.com/sandermuller/package-boost-php)Package-author skills + `lean` / `gitattributes` commandsA Laravel package[`sandermuller/package-boost-laravel`](https://github.com/sandermuller/package-boost-laravel)Laravel-package skills + `McpJsonEmitter`**Your own skill bundle / tooling****`sandermuller/boost-core` directly****Just the sync engine — you supply the skills ← you are here**```
composer require --dev sandermuller/boost-core
```

Coexists with [`laravel/boost`](https://github.com/laravel/boost) in Laravel projects via [`project-boost-laravel`](https://github.com/sandermuller/project-boost-laravel).

[![family overview image](overview_family.png)](overview_family.png)

Quickstart
----------

[](#quickstart)

```
vendor/bin/boost install   # scaffold boost.php + pick agents, vendor allowlist, tags
vendor/bin/boost sync      # fan out to selected agents
vendor/bin/boost sync --check   # dry run — report drift, no writes
```

Config lives at `boost.php` (repo root) or `.config/boost.php`; see [File ownership](#file-ownership) for the layout details. A minimal config:

```
use SanderMuller\BoostCore\Config\BoostConfig;
use SanderMuller\BoostCore\Enums\Agent;

return BoostConfig::configure()
    ->withAgents([Agent::CLAUDE_CODE, Agent::CURSOR]);
```

boost-core runs no install-time code of its own. It's a plain library, not a Composer plugin. Run `vendor/bin/boost sync` yourself (e.g. in CI), or wire the [autosync hook](#automating-the-sync) to re-sync on `composer install`.

What you get
------------

[](#what-you-get)

`laravel/boost``boost-core`Framework scopeLaravel only**Any PHP** (Laravel, Symfony, plain-PHP, packages)Skill sourcesbundled + `.ai/skills/``.ai/skills/` + Composer packages (`resources/boost/skills/`) + `withRemoteSkills()` + `withAllowedVendors()` filterTag filteringnone`withTags()` subset ruleRemote skill sourcesnone`withRemoteSkills()` — GitHub bundles + path importsUser-scope syncnone`boost sync --scope=user` for globally-installed CLI toolsOrigin tracingnone`boost where` + `boost where --diff=` (host / vendor / remote / shadow)Doctor / path-repo auditnone`boost doctor`, `boost doctor --check-versions``.ai/commands/` fan-outnoneper-agent argument transpilation across 7 emit targetsProject ConventionsnoneJSONSchema-validated slot fill-in via `boost validate` / `boost slots`The MCP server (Model Context Protocol) + Laravel docs API are `laravel/boost`'s domain, so boost-core defers to them in Laravel projects (see [`project-boost-laravel`](https://github.com/sandermuller/project-boost-laravel)for coexistence).

Skill sources
-------------

[](#skill-sources)

Skills come from three places, all resolved on the same `composer install / update`lifecycle and fanned out side by side:

1. **Host** — your project's own `.ai/skills/`.
2. **Vendor packages** — any Composer package that ships `resources/boost/skills//SKILL.md`. Allowlist the vendor to pick it up:

    ```
    return BoostConfig::configure()
        ->withAllowedVendors(['vendor/package'])
        ->withAgents([Agent::CLAUDE_CODE]);
    ```

    This is how a team distributes one curated skill set across many repos: author once in a package, allowlist everywhere. [`sandermuller/boost-skills`](https://github.com/sandermuller/boost-skills) is one example of the pattern.
3. **Remote sources** — GitHub repos shipping `.skill` release bundles or skill subdirs, declared with `withRemoteSkills()` (below).

### Remote skill sources

[](#remote-skill-sources)

```
use SanderMuller\BoostCore\Skills\Remote\RemoteSkillSource;

return BoostConfig::configure()
    ->withAgents([Agent::CLAUDE_CODE])
    ->withRemoteSkills([
        // Bundle mode — fetch the named `.skill` release asset and unzip it.
        RemoteSkillSource::githubBundle('peterfox/agent-skills', 'v1.2.0', [
            'composer-upgrade',
            'phpstan-developer',
        ]),

        // Path mode — fetch the repo tarball at a ref and extract named subdirs.
        // `.` covers a whole-repo-is-one-skill layout.
        RemoteSkillSource::githubPath('mattpocock/skills', 'main', [
            'grill-with-docs' => 'skills/engineering/grill-with-docs',
        ]),
    ]);
```

Each fetched skill fans out exactly like host and vendor skills: same layout, same `withTags()` filtering, same `withExcludedSkills(['/:'])`deny-list. Removing an entry prunes its output on the next sync.

- **Cache.** Bundles and tarballs land under `${BOOST_CACHE_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}}/boost/remote-skills/`. Pinned refs (a tag or 40-char SHA) cache forever; moving refs (`main`, a branch) re-resolve every 24h.
- **Offline.** `boost sync --check` never hits the network. `boost doctor` lists every source, flags moving refs, and reports cache presence.
- **Rate limit.** Anonymous GitHub caps at 60 req/h. Set `BOOST_GITHUB_TOKEN`(any token with `public_repo` scope) to lift it to 5000/h. Cold CI runs and `boost doctor` over many sources need it.
- **Trust.** Sources are opt-in by full path: `peterfox/agent-skills:composer-upgrade`grants access to nothing else in the repo. Pin to a tag or SHA in production; moving refs are convenient, but a source-side push silently changes what lands. Archive extraction rejects path traversal, absolute paths, symlinks, and oversized payloads (200 MB total / 50 MB per file / 10000 entries), and any violation rejects the whole source rather than extracting part of it.
- **Strict mode.** `BOOST_REMOTE_STRICT=1` escalates any source failure to a sync-aborting error. Default is warn-and-skip.

**Publishing a source** for remote consumption: treat the `SKILL.md` frontmatter `name` as durable public API (renaming breaks moving-ref consumers), keep source dirs symlink-free (extraction rejects any symlinked entry), and align `metadata.boost-tags` with the family's tag vocabulary.

Tag filtering
-------------

[](#tag-filtering)

Vendor skills can be scoped to projects that want them, so a project with no Jira work never receives a `jira-triage` skill (and its `description` never pollutes the agent's skill-selection index).

A skill declares tags in its `SKILL.md` frontmatter:

```
---
name: jira-triage
description: Triage and label incoming Jira issues.
metadata:
  boost-tags: "php jira"
---
```

A project declares the tags it wants in `boost.php`:

```
use SanderMuller\BoostCore\Enums\Tag;

return BoostConfig::configure()
    ->withAgents([Agent::CLAUDE_CODE])
    ->withTags([Tag::Php, Tag::Jira])        // Tag enum cases or raw strings
    ->withExcludedSkills(['acme/pack:unwanted-skill'])
    ->withExcludedGuidelines(['acme/pack:unwanted-guideline']);
```

**The rule:** a vendor skill ships only when *every* tag in its `boost-tags` is among the project's `withTags()` (`skillTags ⊆ projectTags`). An untagged skill always ships, so the feature is inert until skills and projects opt in. `withExcludedSkills()` drops a specific `vendor/package:skill-name` regardless of tags. Vendor **guidelines** filter the same way, tagged either by `metadata.boost-tags`or a sidecar `resources/boost/guidelines/.boost-tags.yaml` manifest (for guidelines that must stay frontmatter-free for `laravel/boost`).

The `Tag` enum is just a convenience; the vocabulary is open, and any string is a valid tag. `vendor/bin/boost tags` lists every tag installed skills/guidelines declare, which your `withTags()` filters out, and what to add to receive them; `boost install`'s interactive picker offers the same. When sync drops tagged skills because `withTags()` is empty, it prints a one-line nudge at `boost tags`.

Warning

Adding a tag to an **already-shipped** skill is consumer-breaking: every project that hasn't declared that tag loses the skill. Treat it as a breaking change.

Use `boost where` to trace where every resolved skill, guideline, and command comes from (host / vendor / remote), with host-override shadowing annotated inline. `boost where --diff=` prints a unified diff between a host override and the vendor copy it shadows.

Commands
--------

[](#commands)

`.ai/commands/*.md` holds reusable prompt templates: the slash-command files agents surface in their palette. `boost sync` fans each out to the seven agents with a command surface:

AgentCommand targetClaude Code`.claude/commands/`Cursor`.cursor/commands/`Copilot`.github/prompts/` (as `.prompt.md`)Junie`.junie/commands/`OpenCode`.opencode/commands/`Amp`.agents/commands/`Kiro`.kiro/skills//SKILL.md` (slash-command)Codex and Gemini have no committable command target boost-core can write into (Codex's prompts are deprecated/personal-only, Gemini uses TOML). When `.ai/commands/` is populated and one of those agents is selected, `boost doctor`prints the manual authoring path so the gap isn't silent. Override the source dir with `->withCommandsPath(...)`.

**Argument placeholders are transpiled per-agent.** Author once using the canonical syntax: `$ARGUMENTS` (unsplit), `$1`/`$2`/… (positional), `$name` (named, optionally declared in frontmatter `arguments:`), and `\$` escapes for literals. On sync, boost-core converts each to the agent's native shape (Claude `$0`-indexed, Copilot `${input:…}`, and so on). Cursor and Amp have no placeholder support and emit verbatim with a warning. The bundled `command-arguments` skill documents the full table.

Skill rendering
---------------

[](#skill-rendering)

Skill files default to plain markdown (`SKILL.md`). For template-flavored content (Blade, Twig, anything needing a render step), register a `SkillRenderer` in `boost.php`:

```
use SanderMuller\ProjectBoostLaravel\Rendering\BladeRenderer;

return BoostConfig::configure()
    ->withAgents([Agent::CLAUDE_CODE])
    ->withSkillRenderers([new BladeRenderer]);
```

The dispatcher matches longest-extension-first, so a `BladeRenderer` claiming `blade.php` handles `SKILL.blade.php`. The built-in `PassthroughRenderer` always handles `.md`. Render failures default to warn-and-skip (recorded in `SyncResult::errors`); `BOOST_RENDER_STRICT=1` escalates the first failure to a sync-aborting error. A source whose extension has **no** registered renderer (e.g. a `SKILL.blade.php` with no `BladeRenderer`) is flagged by `boost sync` and `boost doctor`. Register a renderer for it, or rename it to `SKILL.md`.

The `SkillRenderer` contract is `@api` (locked at 1.0). Plugin authors writing renderers, `FileEmitter`s, or a `BoostWrapperContract` should work from [`PUBLIC_API.md`](PUBLIC_API.md), which pins the frozen contract surface.

Automating the sync
-------------------

[](#automating-the-sync)

boost-core ships no Composer plugin, so a `composer install` re-sync is opt-in. Pick the entry point that fits:

Entry pointUse for`BoostAutoSync::run``post-install-cmd` / `post-update-cmd` hooks — silent on a no-op`BoostAutoSync::runWithSummary`User-invoked scripts (`composer sync-ai`) — prints a summary always`BoostAutoSync::syncUserScopeOnce`A globally-installed CLI tool self-syncing its own bundled skillsAll three honor `BOOST_SKIP_AUTOSYNC=1`.

### Composer hook (consumer project)

[](#composer-hook-consumer-project)

```
"scripts": {
    "post-install-cmd": ["SanderMuller\\BoostCore\\Scripts\\BoostAutoSync::run"],
    "post-update-cmd":  ["SanderMuller\\BoostCore\\Scripts\\BoostAutoSync::run"],
    "sync-ai":          ["SanderMuller\\BoostCore\\Scripts\\BoostAutoSync::runWithSummary"]
}
```

`run` checks `Event::isDevMode()`, resolves the bin-dir, runs `vendor/bin/boost sync`, surfaces non-zero exits through Composer's IO, and is **silent on a true no-op**(`wrote=0, deleted=0`). Output appears only when something changed or errored. `runWithSummary` prints the one-line success summary on *every* sync, including the no-ops `run` keeps quiet (useful when debugging "did the hook fire?"). Both work on Windows + Unix.

Important

**On Laravel + [`project-boost-laravel`](https://github.com/sandermuller/project-boost-laravel)**, use `@php artisan project-boost:sync` instead of `BoostAutoSync::run`. The artisan path runs through the Laravel container, which bootstraps `BladeRenderer`and delivers laravel/boost's bundled skills to every agent. The bare-CLI path bypasses both. See `project-boost-laravel`'s install guide for the canonical `scripts` shape.

### CLI tool you publish (self-sync from bin script)

[](#cli-tool-you-publish-self-sync-from-bin-script)

A tool installed with `composer global require` keeps its own bundled skills current by self-syncing from its bin script:

```
#!/usr/bin/env php
