diff --git a/docs/svgs/features.svg b/docs/svgs/features.svg
index f8b7b88..47da459 100644
--- a/docs/svgs/features.svg
+++ b/docs/svgs/features.svg
@@ -42,32 +42,32 @@
-
Dependency aggregation
-
Collects frontend requirements from installed Composer packages
+
Automatic selection
+
Detects one native lockfile, then an available executable
-
Native manager support
-
Delegates solving to Bun, Deno, npm, pnpm, or Yarn
+
Configurable recovery
+
Restores package.json and Composer lock/vendor state after failures
-
Automatic selection
-
Detects one native lockfile, then an available executable
+
Defensive I/O
+
Throws an explicit RuntimeException on read, write, or remove failures
-
Flexible project roots
-
Supports package.json outside Composer root via root-package-json-dir
+
Dependency aggregation
+
Collects frontend requirements from installed Composer packages
-
Protected mock directory
-
Validates paths and ownership before recursive replacement
+
Flexible project roots
+
Supports package.json outside Composer root via root-package-json-dir
-
Configurable recovery
-
Restores package.json and Composer lock/vendor state after failures
+
Native manager support
+
Delegates solving to Bun, Deno, npm, pnpm, or Yarn
-
Defensive I/O
-
Throws an explicit RuntimeException on read, write, or remove failures
+
Protected mock directory
+
Validates paths and ownership before recursive replacement
Stable execution
diff --git a/docs/testing.md b/docs/testing.md
index 887d958..b84c55a 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -4,16 +4,24 @@ This package provides a consistent set of [Composer](https://getcomposer.org/) s
Tool references:
+- [Actionlint](https://github.com/rhysd/actionlint) for GitHub Actions workflow linting.
+- [Codespell](https://github.com/codespell-project/codespell) for spelling checks.
- [Composer Require Checker](https://github.com/maglnet/ComposerRequireChecker) for dependency definition checks.
- [Easy Coding Standard (ECS)](https://github.com/easy-coding-standard/easy-coding-standard) for coding standards.
+- [EditorConfig Checker](https://github.com/editorconfig-checker/editorconfig-checker) for EditorConfig compliance.
+- [Gitleaks](https://github.com/gitleaks/gitleaks) for secret scanning.
- [Infection](https://infection.github.io/) for mutation testing.
+- [Markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) for Markdown linting.
- [PHPStan](https://phpstan.org/) for static analysis.
- [PHPUnit](https://phpunit.de/) for unit tests.
+- [Prettier](https://prettier.io/) for Markdown and YAML formatting checks.
- [Rector](https://github.com/rectorphp/rector) for automated refactoring.
+- [Yamllint](https://github.com/adrienverge/yamllint) for YAML linting.
+- [Zizmor](https://github.com/zizmorcore/zizmor) for GitHub Actions security audits.
## Automated refactoring (Rector)
-Run Rector to apply automated code refactoring.
+Run Rector to apply automated code refactoring. This command modifies files.
```bash
composer rector
@@ -22,12 +30,18 @@ composer rector
Validate Rector without modifying files.
```bash
-composer rector -- --dry-run
+composer rector-check
```
## Coding standards (ECS)
-Run Easy Coding Standard (ECS) and apply fixes.
+Check coding standards without modifying files.
+
+```bash
+composer ecs-check
+```
+
+Apply coding standard fixes. This command modifies files.
```bash
composer ecs
@@ -55,6 +69,29 @@ Run mutation testing with static analysis enabled.
composer mutation-static
```
+## Repository quality checks
+
+Make actionlint (and its ShellCheck helper), codespell, EditorConfig Checker, markdownlint-cli2, Prettier and yamllint
+available on `PATH`. Run all repository quality checks:
+
+```bash
+composer quality
+```
+
+Each tool can also be run separately:
+
+```bash
+composer actionlint
+composer codespell
+composer editorconfig-checker
+composer markdownlint
+composer prettier
+composer yamllint
+```
+
+The codespell, EditorConfig Checker and Markdownlint configurations are in `.github/linters`. Prettier uses
+`.prettierignore` as its ignore path; yamllint checks `.github`.
+
## Static analysis (PHPStan)
Run static analysis.
@@ -71,6 +108,25 @@ Run the full test suite.
composer tests
```
+## Workflow security checks
+
+Make zizmor and Gitleaks available on `PATH`. Run both checks, or either one separately:
+
+```bash
+composer security
+composer zizmor
+composer gitleaks
+```
+
+Zizmor uses the pedantic persona and may need GitHub API access for online audits. To request an offline audit:
+
+```bash
+composer zizmor -- --offline
+```
+
+Report offline limitations and hosted CI results separately. Gitleaks scans Git history with the repository's
+configuration. Review findings instead of disabling the checks.
+
## Passing extra arguments
Composer scripts support forwarding additional arguments using `--`.
@@ -90,9 +146,7 @@ composer static -- --memory-limit=512M
## Next steps
- ๐ [Getting started](index.md)
-- โ๏ธ [Configuration reference](config.md)
-- ๐ก [Usage guide](usage.md)
-- ๐ [Events reference](events.md)
-- โ [Frequently asked questions](faqs.md)
-- โฌ๏ธ [Upgrade guide](../UPGRADE.md)
-- ๐ [README](../README.md)
+- ๐ก [Usage guide and examples](usage.md)
+- ๐ค [AI agents guide](agents.md)
+
+[Back to documentation](../README.md#documentation)
diff --git a/docs/usage.md b/docs/usage.md
index 0e70678..8cd8666 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -31,9 +31,40 @@ The project may provide its own frontend dependencies in `package.json`:
}
```
-During Composer install and update operations, Foxy merges eligible Composer package assets into this file and runs
-the selected frontend manager. Existing non-Foxy dependencies are preserved. With `"manager": "native"`, Foxy
-installs the merged file itself and writes `foxy.lock`; see [Native manager](config.md#native-manager).
+During Composer install and update operations, Foxy merges eligible Composer package assets into this file and runs the
+selected frontend manager. Existing non-Foxy dependencies are preserved. With `"manager": "native"`, Foxy installs the
+merged file itself and writes `foxy.lock`; see [Native manager](config.md#native-manager).
+
+### Merge manifests without running a frontend manager
+
+```json
+{
+ "config": {
+ "foxy": {
+ "run-asset-manager": false
+ }
+ }
+}
+```
+
+Foxy still merges eligible packages; it skips automatic manager probing and execution. This is not an asset install, and
+an explicit `composer foxy:audit` still validates and runs the selected manager.
+
+### Use a project manifest in `web/`
+
+```json
+{
+ "config": {
+ "foxy": {
+ "manager": "npm",
+ "root-package-json-dir": "web"
+ }
+ }
+}
+```
+
+Foxy reads and writes `web/package.json` and runs npm from `web/`. Paths are relative to the Composer project. See
+[package.json directory](config.md#packagejson-directory).
## Security auditing
@@ -49,22 +80,22 @@ report version 2 starting with npm 10.9.8, the current report schemas emitted by
npm registry's bulk advisories document read by the `native` manager; legacy report formats are rejected instead of
being interpreted heuristically.
-With the `native` manager, `composer foxy:audit` reads `foxy.lock` and asks the configured `registry-url` for
-advisories in one request to npm's bulk advisories endpoint (`POST
/-/npm/v1/security/advisories/bulk`),
-sending every locked registry package with its version; local `file:` packages are not sent, and a lock without
-registry packages is clean without a request. `--no-dev` is rejected with status `2` because the native manager
-installs development dependencies together with the others. A registry that does not implement the endpoint fails
-the audit with status `2`.
+With the `native` manager, `composer foxy:audit` reads `foxy.lock` and asks the configured `registry-url` for advisories
+in one request to npm's bulk advisories endpoint (`POST /-/npm/v1/security/advisories/bulk`), sending
+every locked registry package with its version; local `file:` packages are not sent, and a lock without registry
+packages is clean without a request. `--no-dev` is rejected with status `2` because the native manager installs
+development dependencies together with the others. A registry that does not implement the endpoint fails the audit with
+status `2`.
Deno does not provide a machine-readable report. Foxy runs `deno audit --level=low` with `NO_COLOR=1` against
-`deno.lock` and parses its text report strictly. The parser is validated against Deno 2.9.7 and returns status `2`
-when the report layout is not recognized instead of guessing. Deno has no configuration setting that filters audit
-results, and Foxy does not forward manager options to the audit command.
+`deno.lock` and parses its text report strictly. The parser is validated against Deno 2.9.7 and returns status `2` when
+the report layout is not recognized instead of guessing. Deno has no configuration setting that filters audit results,
+and Foxy does not forward manager options to the audit command.
Foxy reports every advisory returned by the manager. `--audit-level` controls only the CI exit threshold:
```bash
-composer foxy:audit --audit-level=high
+composer foxy:audit --audit-level=high --format=summary
```
Valid levels are `low`, `moderate`, `high`, and `critical`; the default is `low`. Use `--no-dev` to audit only the
@@ -77,8 +108,8 @@ composer foxy:audit --no-dev
For npm workspace roots, Foxy explicitly includes every workspace and the root package so ambient npm workspace
selection cannot silently narrow the audited lock graph.
-`deno audit` cannot exclude development dependencies, so a Deno audit with `--no-dev` returns status `2`. Run the
-Deno audit without `--no-dev` to cover the complete dependency graph.
+`deno audit` cannot exclude development dependencies, so a Deno audit with `--no-dev` returns status `2`. Run the Deno
+audit without `--no-dev` to cover the complete dependency graph.
Foxy also overrides pnpm and Yarn settings that could silently filter the requested dependency graph or known
advisories. Bun 1.4 cannot safely reset every inherited scope setting while retaining project registry and
@@ -108,8 +139,8 @@ The npm registry's audit data normally identifies advisories by a numeric ID and
[GitHub global security advisories API](https://docs.github.com/en/rest/security-advisories/global-advisories). The
lookups run concurrently through Composer's HTTP downloader, at most 12 at once by default; the
`COMPOSER_MAX_PARALLEL_HTTP` environment variable changes that limit. A `github-oauth` token for `github.com` in
-Composer's `auth.json` applies to these requests and raises GitHub's rate limit. A failed optional lookup marks only
-its own advisory as `unavailable` and does not discard the finding itself or change its audit exit status.
+Composer's `auth.json` applies to these requests and raises GitHub's rate limit. A failed optional lookup marks only its
+own advisory as `unavailable` and does not discard the finding itself or change its audit exit status.
Bun can skip packages from a registry that does not answer its audit request while still returning a successful native
status. Foxy therefore treats any diagnostic emitted by `bun audit --json` as an unreliable partial report and returns
@@ -131,15 +162,15 @@ Gate a production build on high and critical findings while retaining a compact
composer foxy:audit --no-dev --audit-level=high --format=summary
```
-The explicit command still runs when `run-asset-manager` is `false`; that setting controls automatic install and
-update execution, not a user-requested audit. Foxy must be enabled, the selected binary must satisfy its supported
-version constraint, and the manager's own lockfile must exist. The `native` manager has no binary, so only Foxy and
-`foxy.lock` are required for it.
+The explicit command still runs when `run-asset-manager` is `false`; that setting controls automatic install and update
+execution, not a user-requested audit. Foxy must be enabled, the selected binary must satisfy its supported version
+constraint, and the manager's own lockfile must exist. The `native` manager has no binary, so only Foxy and `foxy.lock`
+are required for it.
## Library setup
-A Composer library becomes eligible for Foxy when it contains the selected manager's package definition and uses one
-of the activation methods below.
+A Composer library becomes eligible for Foxy when it contains the selected manager's package definition and uses one of
+the activation methods below.
### Required Foxy dependency
@@ -165,8 +196,8 @@ Use a development dependency when Foxy is optional for library development:
}
```
-The consuming application must still require and authorize Foxy because dependencies of a library are not installed
-from its `require-dev` section.
+The consuming application must still require and authorize Foxy because dependencies of a library are not installed from
+its `require-dev` section.
### Activation through Composer extra
@@ -205,18 +236,18 @@ The embedded manifest must resolve inside the installed library's Composer direc
## Embedded package metadata
-Foxy creates a local package representation for each eligible Composer dependency. It copies only metadata required
-for dependency resolution and compatibility checks. Package lifecycle `scripts`, `devDependencies`, executable
-definitions, entry points, and unrelated publishing metadata are not propagated to the consuming application.
+Foxy creates a local package representation for each eligible Composer dependency. It copies only metadata required for
+dependency resolution and compatibility checks. Package lifecycle `scripts`, `devDependencies`, executable definitions,
+entry points, and unrelated publishing metadata are not propagated to the consuming application.
Library authors should declare runtime frontend requirements in `dependencies`, `optionalDependencies`, or
-`peerDependencies`. Any build step needed to publish those assets should run before the Composer package is released,
-or be configured explicitly by the consuming application.
+`peerDependencies`. Any build step needed to publish those assets should run before the Composer package is released, or
+be configured explicitly by the consuming application.
## Manually enabling or disabling packages
-The root application can use `config.foxy.enable-packages` to include a package that does not declare Foxy or to
-exclude a package that does. See [Package selection](config.md#package-selection).
+The root application can use `config.foxy.enable-packages` to include a package that does not declare Foxy or to exclude
+a package that does. See [Package selection](config.md#package-selection).
## Behavior and guarantees
@@ -226,23 +257,23 @@ exclude a package that does. See [Package selection](config.md#package-selection
- `root-package-json-dir` controls project reads, writes, and manager working directory.
- Manager execution does not change the PHP process working directory.
- The `native` manager installs one version per package into a flat directory (`node_modules` unless
- `native-install-dir` says otherwise), verifies every tarball against its registry integrity value, and reinstalls
- from `foxy.lock` without registry metadata requests.
+ `native-install-dir` says otherwise), verifies every tarball against its registry integrity value, and reinstalls from
+ `foxy.lock` without registry metadata requests.
- Asset restoration occurs when package merging fails or manager execution throws or returns a non-zero result, and
`fallback-asset` is enabled. It runs before Composer restoration.
- Composer lock and vendor restoration occurs after any solve exception or non-zero manager result when
`fallback-composer` is enabled.
- Read, write, and remove failures are reported through exceptions instead of being silently ignored.
-Composer restoration does not include the root `composer.json` bytes. Review that file after a failed
-`composer require` or `composer remove` operation.
+Composer restoration does not include the root `composer.json` bytes. Review that file after a failed `composer require`
+or `composer remove` operation.
## Next steps
- ๐ [Getting started](index.md)
- โ๏ธ [Configuration reference](config.md)
-- ๐ [Events reference](events.md)
- โ [Frequently asked questions](faqs.md)
-- ๐งช [Testing guide](testing.md)
-- โฌ๏ธ [Upgrade guide](../UPGRADE.md)
-- ๐ [README](../README.md)
+- ๐ [Events reference](events.md)
+- ๐ค [AI agents guide](agents.md)
+
+[Back to documentation](../README.md#documentation)
diff --git a/skills/php-forge-foxy/SKILL.md b/skills/php-forge-foxy/SKILL.md
new file mode 100644
index 0000000..11fe5dc
--- /dev/null
+++ b/skills/php-forge-foxy/SKILL.md
@@ -0,0 +1,66 @@
+---
+name: php-forge-foxy
+description:
+ Use when configuring php-forge/foxy in a Composer application or library,
+ selecting an asset manager, troubleshooting package merges, or auditing
+ frontend dependencies.
+license: BSD-3-Clause
+---
+
+# Configure Foxy
+
+Foxy is a Composer plugin that merges eligible Composer libraries' frontend manifests into an application and invokes a
+selected frontend manager, or installs assets itself with the native manager. Match the installed revision before
+applying these instructions.
+
+## Establish the revision and scope
+
+For a root checkout, inspect `composer show --self --format=json`, its HEAD and working tree. For an installed
+dependency, resolve the configured vendor directory and inspect `composer/installed.php` for `php-forge/foxy`
+(`install_path`, `pretty_version`, and selected `reference`), then check `composer.lock`. A symlinked checkout can
+differ from both metadata and lock: inspect its HEAD and current files. Uncommitted changes have no matching revision.
+Use documentation from the matching revision; if unavailable, inspect installed source and report the gap.
+
+Read only the relevant canonical local guide: `docs/index.md` for requirements and installation, `docs/config.md` for
+configuration, `docs/usage.md` for workflows and examples, `docs/events.md` for extension events, or `docs/faqs.md` for
+troubleshooting. For an upgrade, use `UPGRADE.md` for the incompatibility summary and `docs/migration.md` for detailed
+actions. Guides can be absent from Composer distributions; use the matching repository revision. Check `composer.json`,
+the root and library manifests and their lockfiles before proposing commands. Preserve existing project dependencies and
+manager choice.
+
+## Choose a setup
+
+- The application must require `php-forge/foxy` and explicitly authorize it in Composer `allow-plugins`. A library's
+ `require-dev` does not install Foxy for consumers. A library may declare `extra.foxy=true`; its consumer still needs
+ the plugin.
+- Select `bun`, `deno`, `npm`, `pnpm`, `yarn`, or `native` explicitly for reproducible CI. Without a selection, one
+ recognized lockfile chooses the manager; multiple lockfiles cause an error. Native is selected only explicitly or by a
+ single `foxy.lock`, never by executable discovery.
+- `run-asset-manager=false` skips automatic binary probing and execution but still updates manifests; an explicit
+ `composer foxy:audit` still executes the selected manager. Do not present this mode as an install or an audit.
+- Native mode requires PHP zlib, owns its install directory and removes unlisted non-dot entries. Do not share that
+ directory with another installer. It does not run lifecycle scripts, create executable links, support nested
+ incompatible versions, or accept Git, URL, alias, workspace or link specifications. Use a JavaScript manager when
+ those features are needed.
+- `root-package-json-dir` sets the application manifest and manager working directory. In a library it is relative to
+ the installed library and must remain inside it. Deno workspace members must remain inside the root package directory.
+
+## Handle failures and audit
+
+Manager version overrides narrow rather than widen built-in support. Inspect the selected revision's requirements and
+actual binary before changing a constraint. `composer foxy:audit` needs the selected lockfile; exit 0 means no finding
+reached the threshold, 1 means a finding did, and 2 means a reliable audit could not be produced. Deno and native reject
+`--no-dev`. Do not treat status 2 as a clean audit or run an automatic fix as a substitute.
+
+Foxy can restore the asset manifest and Composer lock/vendor state after a solve failure, but the root `composer.json`
+bytes are not part of the rollback. Inspect it after failed `require` or `remove`. Never point generated asset storage
+at user-owned data; Foxy resets its managed directory and validates ownership.
+
+## Verify behavior
+
+Test in a disposable project or fixture before touching real lockfiles. Verify the effective manager, merged manifest
+and preserved unrelated dependencies, then repeat the same operation to check idempotence. Exercise the applicable error
+path (multiple lockfiles, missing binary, invalid options or unsupported native specification) and confirm the error is
+surfaced and expected state is preserved. For audit changes, test statuses 0, 1 and 2 independently. Run the consuming
+project's checks and report whether actual manager execution, native skill discovery and activation were verified;
+reading this file alone proves neither activation nor a successful installation.