diff --git a/.changelog/bundled-consumer-skill.md b/.changelog/bundled-consumer-skill.md new file mode 100644 index 0000000..fac0c33 --- /dev/null +++ b/.changelog/bundled-consumer-skill.md @@ -0,0 +1,6 @@ +--- +category: added +type: minor +--- + +Bundle the fast-forward-http consumer skill with executable examples, optional installation guidance and Composer discovery metadata. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a7c5e5b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,15 @@ +# fast-forward-http maintenance + +This repository owns `fast-forward/http`. Read Composer metadata, source and +tests before changing public integration. Preserve unrelated work. Run +`composer install`, `composer validate --strict` and `composer dev-tools`. +Keep merge, release and deployment decisions with the maintainer. Ordinary +source/resources share this root contract; consumer procedures live in SKILL.md. + +## Bundled consumer skill + +Read [the consumer skill](resources/skills/fast-forward-http/SKILL.md) when changing application integration +examples, consumer guides or the skill. Keep its version contract, references +and executable examples aligned with the supported public API. The canonical +distributable source is `resources/skills`; it needs no installed copy or host +alias. Repository maintenance continues to follow this AGENTS.md. diff --git a/README.md b/README.md index faf6f47..e0626fd 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,15 @@ See the documentation sources in [`docs/`](docs/) for: Fast Forward HTTP is licensed under the [MIT license](LICENSE). +## Consumer agent skill + +The package includes [fast-forward-http](resources/skills/fast-forward-http/SKILL.md) for application integration. +It supports `fast-forward/http:^1.2`; inspect the installed version. +See [skill installation and lifecycle](resources/skills/fast-forward-http/references/installation.md) for +optional project synchronization with `llm/skills ^1.15`, guarded manual +project/global copies and a pinned Skills CLI alternative. Shipping files does +not install tooling or activate the skill in an agent session. + ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for changelog fragments, local checks and the release-review flow. diff --git a/composer.json b/composer.json index f200973..a7aa46e 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,9 @@ "fast-forward/dev-tools": "dev-main", "llm/skills": "^1.15" }, + "suggest": { + "llm/skills": "Optionally synchronize the bundled consumer skill in your project." + }, "minimum-stability": "stable", "autoload": { "psr-4": { @@ -56,6 +59,9 @@ }, "grumphp": { "config-default-path": "vendor/fast-forward/dev-tools/grumphp.yml" + }, + "skills": { + "source": "resources/skills" } }, "scripts": { diff --git a/resources/skills/fast-forward-http/LICENSE b/resources/skills/fast-forward-http/LICENSE new file mode 100644 index 0000000..aa2c238 --- /dev/null +++ b/resources/skills/fast-forward-http/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2025 Felipe Sayão Lobato Abreu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/resources/skills/fast-forward-http/SKILL.md b/resources/skills/fast-forward-http/SKILL.md new file mode 100644 index 0000000..ad467d6 --- /dev/null +++ b/resources/skills/fast-forward-http/SKILL.md @@ -0,0 +1,88 @@ +--- +name: fast-forward-http +description: Wire Fast Forward HTTP providers into an application container and obtain PSR-17 factories and a PSR-18 client. +license: MIT; see LICENSE +compatibility: Supports fast-forward/http ^1.2 and PHP ^8.3. Requires Composer and declared extensions; inspect the installed version. Earlier releases are outside this contract. +metadata: + author: Fast Forward + api-baseline: fast-forward/http ^1.2 +--- + +# Compose HTTP services + +Use this skill for an application consuming `fast-forward/http:^1.2`. +Read its instructions, manifest/lockfile and installed source before editing. +Run `composer show fast-forward/http`. For versions outside this contract, +inspect matching documentation; do not silently upgrade the application. + +## First working integration + +Requires PHP ^8.3, Composer's application autoloader and this package's runtime +dependencies. +With authority to install the dependency: + +```sh +composer require 'fast-forward/http:^1.2' +``` + +Create `app.php` in the application root alongside composer.json/vendor: + +```php + [HttpServiceProvider::class]]); +$container = container($config); +$response = $container->get(ResponseFactoryInterface::class)->createResponse(202); +$request = $container->get(RequestFactoryInterface::class)->createRequest('GET', 'https://example.invalid/'); +$client = $container->get(ClientInterface::class); +if ($response->getStatusCode() !== 202 || $request->getMethod() !== 'GET' || !$client instanceof ClientInterface) { + throw new RuntimeException('HTTP composition failed.'); +} + +echo "integration ready\n"; +``` + +Run `php app.php`. It throws on a wrong result; success prints +`integration ready`. It uses no production data and starts no server. + +## Wiring and failure boundaries + +Store the raw provider list under `FastForward\Container\ContainerInterface::class`; the ConfigContainer exposes this as `config.FastForward\Container\ContainerInterface`. Pass the configuration to `container($config)`, then pass the returned container to the application's runtime. The provider composes HttpMessageFactoryServiceProvider and HttpClientServiceProvider. It supplies client/factories and a globals-based server request, not an HTTP server, route dispatcher or response emitter. + +Without application configuration use `container(HttpServiceProvider::class)`. Resolve the extended `FastForward\Http\Message\Factory\ResponseFactoryInterface` for payload helpers, and the PSR ResponseFactoryInterface for ordinary PSR-17 creation. They are distinct services. + +Put a custom ArrayServiceProvider before defaults to override an interface. The first source that successfully resolves the id wins; service-provider products are cached. Request/response factory calls create message objects, while resolving the globals-based ServerRequestInterface service can cache the request: build a fresh request explicitly for long-lived worker iterations. Missing services raise PSR-11 not-found errors; non-PSR factory failures propagate. Do not send the example.invalid request; it tests composition without a network dependency. + +The helper tries aggregate sources in registration order. A PSR container or +not-found exception from one source permits fallback to another source; other +throwables propagate. Source priority is therefore the first successful resolution, +not a guarantee that an earlier broken factory terminates lookup. Nested provider +list errors during helper composition are caught; if services are absent, inspect +the raw configuration and resolve the provider directly to diagnose the failure. + +## Verify integration + +Run the [complete example](examples/integration.php) against the application's +autoloader in a disposable process. It checks success and negative cases: + +```sh +php /path/to/fast-forward-http/examples/integration.php ./vendor/autoload.php +``` + +Expected final marker: `fast-forward-http: ok`. Keep application-specific regression +tests in its existing PHPUnit suite, with each fixture class in its same-named +file. File copying, host discovery and completed agent tasks are different checks. +For optional copying/synchronization load +[installation and lifecycle](references/installation.md). diff --git a/resources/skills/fast-forward-http/examples/integration.php b/resources/skills/fast-forward-http/examples/integration.php new file mode 100644 index 0000000..7473388 --- /dev/null +++ b/resources/skills/fast-forward-http/examples/integration.php @@ -0,0 +1,29 @@ + [HttpServiceProvider::class]]); +$container = container($config); +$response = $container->get(ResponseFactoryInterface::class)->createResponse(202); +$request = $container->get(RequestFactoryInterface::class)->createRequest('GET', 'https://example.invalid/'); +$client = $container->get(ClientInterface::class); +if ($response->getStatusCode() !== 202 || $request->getMethod() !== 'GET' || !$client instanceof ClientInterface) { + throw new RuntimeException('HTTP composition failed.'); +} +try { + $container->get('application.missing'); + throw new RuntimeException('Missing service unexpectedly resolved.'); +} catch (\Psr\Container\NotFoundExceptionInterface) { +} + +echo "fast-forward-http: ok\n"; diff --git a/resources/skills/fast-forward-http/references/installation.md b/resources/skills/fast-forward-http/references/installation.md new file mode 100644 index 0000000..e9bf848 --- /dev/null +++ b/resources/skills/fast-forward-http/references/installation.md @@ -0,0 +1,167 @@ +# Optional installation of fast-forward-http + +`fast-forward/http:^1.2` includes this complete package under +`resources/skills/fast-forward-http`. Run `composer show fast-forward/http` +and inspect that path before copying. Earlier releases are outside this skill +contract. Composer suggestions do not install anything automatically. Library +installation, discovery-root copies and session activation are separate steps. + +## Team project with llm/skills + +Use Composer 2 and optionally add `llm/skills:^1.15` as development tooling. +Start with a project target for reviewable team configuration. Preserve existing +settings/aliases/approvals when merging these keys into `skills.json` **before** +installing or enabling the plugin: + +```json +{ + "target": ".agents/skills", + "aliases": [], + "discovery": false, + "auto-sync": true, + "vendor-sources": false, + "dependencies": { + "composer": { + "trusted": ["fast-forward/http"], + "trusted-replace": true + } + } +} +``` + +This replaces implicit dependency trust with a narrow donor allowlist, disables +remote vendor sources and requests no host aliases. A deliberate +`fast-forward/*` allowlist grants broader organizational trust. Use the actual +host's project discovery root; Claude Code can choose `.claude/skills`. +Do not erase intentional aliases or other donors in an existing manifest. + +If the application owner has authorized this plugin's execution, merge its +root approval, install it and inspect the report: + +```sh +composer config allow-plugins.llm/skills true +composer require --dev 'llm/skills:^1.15' +composer skills:show +``` + +The donor only suggests the tool; it neither requires it at runtime nor grants +this approval on behalf of consumers. Commit the chosen manifest, skills.json, +application lockfile and intended project copies according to project policy. +Approved Composer install/update operations then perform automatic sync. + +For manual timing, choose `auto-sync:false` before installation and use: + +```sh +composer skills:show +composer skills:update fast-forward/http --dry-run +composer skills:update fast-forward/http +``` + +Explicitly naming a donor selects/trusts it for that run. Test automatic trust +without a package argument when that policy is what matters. With Composer +2.10.3 and llm/skills 1.15.0, `--no-scripts` alone still permits automatic copy; +`auto-sync:false` disables automatic sync and `--no-plugins` stops plugin loading. +Verify these behaviors again after changing tool versions. + +## Update and removal boundaries + +A 1.15 sync overwrites incoming same-path files. It retains extra local files +and files removed from the donor's next version. Removing a donor or uninstalling +the plugin does not delete its existing instruction copies. Regard the copy as +donor-owned; keep deliberate local instructions in a separate skill name. +Record the installed donor/tool versions and review diffs before each refresh. + +Inspect cleanup without applying it: + +```sh +composer skills:update --clean --dry-run +``` + +An unscoped clean can remove all target skills, including handwritten ones. +Scoped clean reinstalls the current selection; it cannot locate an absent donor +by historical ownership. Inventory affected directories, back up outside +discovery, and obtain authority before any destructive cleanup. Do not add a +root-wide clean hook. + +## Guarded manual copying + +Choose the existing project root, then copy all files together: + +```sh +skill_source="vendor/fast-forward/http/resources/skills/fast-forward-http" +skill_target=".agents/skills/fast-forward-http" +test -f "$skill_source/SKILL.md" && +test ! -e "$skill_target" && +test ! -L "$skill_target" && +mkdir -p "$(dirname "$skill_target")" && +cp -R "$skill_source" "$skill_target" +``` + +Both existing directories and dangling links are refused. Global use is an +explicit alternative: set skill_target to +`$HOME/.agents/skills/fast-forward-http` for Codex or the configured +`$HOME/.claude/skills/fast-forward-http` for Claude Code. +Do not create a symlink into replaceable vendor content. A globally installed +Composer plugin does not constitute a global instruction copy. + +For a selected manual update/removal, first move only that package into a unique +backup outside discovery: + +```sh +skill_backup="$HOME/.agent-skill-backups/fast-forward-http-$(date +%Y%m%d%H%M%S)-$$" +test -d "$skill_target" && +test ! -L "$skill_target" && +test ! -e "$skill_backup" && +test ! -L "$skill_backup" && +mkdir -p "$(dirname "$skill_backup")" && +mv "$skill_target" "$skill_backup" +``` + +For an update review the new source, then repeat the guarded copy. To roll back, +archive any replacement and restore the saved whole directory to the same +target. A manager-owned copy may return on its next sync until the consumer +changes that donor's trust/dependency configuration. Keep backups out of every +discovery directory. + +## Optional Skills CLI + +Requires npm, Node.js 22.20.0+ and Git for repository sources. The reviewed pin +is skills@1.7.2; evaluate version changes separately. Choose only the actual host: + +```sh +npx --yes skills@1.7.2 add ./vendor/fast-forward/http/resources/skills/fast-forward-http --skill fast-forward-http --agent codex --copy --yes +npx --yes skills@1.7.2 list --agent codex +``` + +Use `--agent claude-code` for that host. Add `--global` to both commands only +when global scope was selected. Preserve/commit intended project copies and the +CLI's project lockfile. For a remote source first set SKILL_REF to a reviewed +immutable commit: + +```sh +npx --yes skills@1.7.2 add "https://github.com/php-fast-forward/http/tree/$SKILL_REF/resources/skills/fast-forward-http" --skill fast-forward-http --agent codex --copy --yes +``` + +Review the new revision and back up local edits before repeating for an update. +Remove the chosen package/scope with: + +```sh +npx --yes skills@1.7.2 remove fast-forward-http --agent codex --yes +# Only for a selected global copy: +npx --yes skills@1.7.2 remove fast-forward-http --agent codex --global --yes +``` + +After every route inspect the complete files, refresh the agent session, +confirm the public name is discovered and perform a bounded consumer task. +A copy, listing and successful task are separate observations. + +Tool contracts: [llm/skills 1.15.0](https://github.com/roxblnfk/skills/tree/1.15.0), +[Composer plugin approval](https://getcomposer.org/doc/06-config.md#allow-plugins) +and [Skills CLI](https://github.com/vercel-labs/skills). + +The Skills CLI 1.7.2 removal report is not proof that the canonical project +`.agents/skills` directory disappeared. A Codex project exercise reported one +removal while that copied package remained. Inspect the actual target and CLI +lockfiles afterwards. If an obsolete copy remains, verify that no other host +uses it, then archive only that selected directory outside discovery using the +manual recovery procedure above. Do not run a blanket clean to compensate. diff --git a/tests/ConsumerSkillTest.php b/tests/ConsumerSkillTest.php new file mode 100644 index 0000000..4278e45 --- /dev/null +++ b/tests/ConsumerSkillTest.php @@ -0,0 +1,47 @@ + + * @license https://opensource.org/licenses/MIT MIT License + * + * @see https://php-fast-forward.github.io/http/ + * @see https://github.com/php-fast-forward/http + * @see https://github.com/php-fast-forward/http/issues + * @see https://datatracker.ietf.org/doc/html/rfc2119 + */ + +namespace FastForward\Http\Tests; + +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Process; + +#[CoversNothing] +final class ConsumerSkillTest extends TestCase +{ + /** + * @return void + */ + public function testBundledConsumerIntegration(): void + { + $root = \dirname(__DIR__); + $process = new Process([ + \PHP_BINARY, + $root . '/resources/skills/fast-forward-http/examples/integration.php', + $root . '/vendor/autoload.php', + ], $root, [ + 'XDEBUG_MODE' => 'off', + ]); + $process->setTimeout(20); + $process->run(); + + self::assertTrue($process->isSuccessful(), $process->getErrorOutput() . $process->getOutput()); + self::assertStringContainsString('fast-forward-http: ok', $process->getOutput()); + } +}