Skip to content

Commit dd4015c

Browse files
committed
docs: technical-writer review fixes (9 findings)
Final pass after the technical-writer review caught 3 must-fix contradictions/omissions and 6 polish items. Must-fix * README claimed glob matching was 'case-insensitive' — directly contradicting both the code (custom regex without /i) and the test suite. The wrong claim also undermined the security rationale. Replaced with the correct case-sensitive description. * composer skills:list-trust was undocumented in README and CHANGELOG despite shipping in this PR. Added to both, with a description of what it shows. * The README's per-package prompt example was stale — missing the [?] help option, the 'change later: composer skills:trust …' recovery hint, and using the wrong [d] description. Synced with the actual prompt text. Should-fix * docs/PRD.md addendum still described the 'composer config --json' hint and the auto-seeding-everything behaviour, both replaced in earlier commits. Rewrote that subsection to match the current first-run policy and added the four-command inventory table. * README 'Upgrading from v0.1.x' listed [a]/[d]/[n] options in inverse order vs the prompt's [n,d,a]. Reordered for visual consistency. * examples/database-analyzer-skill/README.md treated 'type: ai-agent-skill' as the only discovery mechanism. Added a top-of-file note pointing to the library-bundled-skill alternative, and updated the troubleshooting line to reference composer skills:trust. Nice-to-have * TrustDecision had no class docblock; mirrored TrustState's docblock and explained the distinction so maintainers see it from both sides. * README Quick Start step 2 'Skills Auto-Register' didn't mention the trust prompt; users would be surprised when their install paused. Added one bullet pointing at the Trust Model section. * CHANGELOG entry for the [?] help loop in the per-package prompt. 127 tests / 464 assertions still green. PHPStan level 10 clean. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
1 parent 457728c commit dd4015c

5 files changed

Lines changed: 54 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828
- **Root package is auto-trusted** — projects that ship their own `SKILL.md` no longer get prompted to authorize themselves.
2929
- `composer list-skills` now shows trust state (`[allowed]` / `[pending]` / `[denied]`) per skill and a footer count of pending packages. The command is purely informational and never prompts.
3030
- `composer skills:trust <package>` command — allow (`composer skills:trust vendor/foo`), deny (`--deny`), or revoke (`--revoke`) a trust decision without hand-editing `composer.json`. Used as the recovery path for accidental denies and as the canonical fix for non-interactive failures.
31+
- `composer skills:list-trust` command — read-only inventory of every persisted decision in `extra.ai-agent-skill.allow-skills`, with `[allowed]` / `[denied]` and `(exact)` / `(glob)` markers per entry. Companion to `skills:trust`; never prompts, never mutates.
32+
- Trust prompt mirrors Composer's plugin prompt shape (`y`/`n`/`a`/`d`/`?`). The `?` answer shows per-option help and re-prompts. The prompt also includes an inline `composer skills:trust <package>` recovery hint so users have an on-screen breadcrumb if they pick `n` by accident.
3133
- `composer read-skill` now shows trust state in the header and warns when reading content from a pending or denied skill (which is not registered in `AGENTS.md`).
3234
- `SkillTrustManager`, `PackageProvider`, `InstalledVersionsProvider`, `PackageInfo`, and `TrustDecision` abstractions for testability.
3335

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,13 @@ Install any package with type `ai-agent-skill`:
7171
composer require vendor/database-analyzer-skill
7272
```
7373

74-
### 2. Skills Auto-Register
74+
### 2. Skills Auto-Register (after trust)
7575

7676
The plugin automatically:
7777
- Discovers skill packages during `composer install` or `composer update`
78-
- Generates/updates `AGENTS.md` in your project root
79-
- Registers skills in XML format for AI agent consumption
78+
- **Asks once** before registering skills from a package it hasn't seen before — see [Trust Model](#trust-model)
79+
- Generates/updates `AGENTS.md` in your project root with the trusted skills
80+
- Registers them in XML format for AI agent consumption
8081

8182
### 3. Use Skills
8283

@@ -103,8 +104,10 @@ Allow this package to register skills?
103104
[y] Yes — allow & persist (writes to composer.json)
104105
[n] No — deny & persist (suppress future prompts)
105106
[a] Allow for this session only
106-
[d] Discard — do not allow, do not write
107-
(defaults to n) [y,n,a,d]:
107+
[d] Discard — leave undecided, ask again next run
108+
[?] Show details about this choice
109+
(change later with: composer skills:trust vendor/foo [--deny|--revoke])
110+
(defaults to n) [y,n,a,d,?]:
108111
```
109112

110113
Decisions persist under `extra.ai-agent-skill.allow-skills` in your root `composer.json`:
@@ -125,27 +128,28 @@ Decisions persist under `extra.ai-agent-skill.allow-skills` in your root `compos
125128

126129
### Managing trust decisions
127130

128-
Use the dedicated `composer skills:trust` command (preferred):
131+
Use the dedicated commands (preferred):
129132

130133
```bash
131134
composer skills:trust vendor/foo # allow & persist
132135
composer skills:trust vendor/foo --deny # deny & persist
133136
composer skills:trust vendor/foo --revoke # remove from the map (re-prompts next install)
137+
composer skills:list-trust # show every persisted decision
134138
```
135139

136140
Or edit `composer.json` directly. Glob patterns (`vendor/*`) are supported.
137141

138-
> ⚠️ **Glob matching is case-insensitive and `*` matches any characters.** A pattern like `acme/skills-*` also trusts `acme/skills-anything-else` — use globs only for namespaces you fully control. A typo like `acme/sk*` would match a wide range of unrelated packages. Prefer explicit per-package entries when in doubt.
142+
> ⚠️ **Glob matching is case-sensitive and `*` matches any characters within a pattern segment.** Composer normalizes published package names to lowercase, so write trust patterns in lowercase. A pattern like `acme/skills-*` also trusts `acme/skills-anything-else` — use globs only for namespaces you fully control. Exact-string keys always override matching globs, even when the glob was added first. Prefer explicit per-package entries when in doubt.
139143
140144
In non-interactive mode (`composer install --no-interaction`, CI), packages without an explicit decision are skipped with a warning — the plugin never auto-trusts on your behalf. The warning suggests `composer skills:trust <package>` so CI failures are one command away from a fix. `composer list-skills` shows the trust state per skill (`[allowed]` / `[pending]` / `[denied]`) without firing prompts.
141145

142146
### Upgrading from v0.1.x
143147

144148
If you upgrade from v0.1.x with `type: ai-agent-skill` packages already installed, the first install/update after the upgrade triggers a one-time prompt asking how to seed the trust map. Choose:
145149

146-
- **`[a]`** to keep the v0.1.x default (every existing skill package auto-trusted) — fastest, widest trust surface.
147-
- **`[d]`** to auto-trust only the dedicated skill packages your root `composer.json` directly requires; transitive ones still prompt per-package.
148150
- **`[n]`** (default) to auto-trust nothing — every package goes through the per-package prompt during the same install.
151+
- **`[d]`** to auto-trust only the dedicated skill packages your root `composer.json` directly requires; transitive ones still prompt per-package.
152+
- **`[a]`** to keep the v0.1.x default (every existing skill package auto-trusted) — fastest, widest trust surface.
149153

150154
Non-interactive runs (`--no-interaction`, CI) default to `[n]` with a `composer skills:trust <package>` recovery line per affected package.
151155

docs/PRD.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -867,14 +867,33 @@ Discovery now iterates **all** installed packages and selects those that either:
867867

868868
Broadening discovery means any transitive dependency could potentially register skills. To keep the security story intact, the plugin requires per-package opt-in before reading SKILL.md content for inclusion in `AGENTS.md` — the same shape Composer uses for `allow-plugins`:
869869

870-
- **Interactive mode**: `[y,n,a,d]` prompt; `y`/`n` persist to `composer.json`, `a` is session-only, `d` discards.
871-
- **Non-interactive mode**: skip with a `composer config --json` hint (gentler than Composer's hard-fail; CI stays deterministic).
872-
- **Persistence**: `extra.ai-agent-skill.allow-skills` in the root `composer.json`. Glob patterns (`vendor/*`) supported via `BasePackage::packageNameToRegexp()`.
870+
- **Interactive mode**: `[y,n,a,d,?]` prompt; `y`/`n` persist to `composer.json`, `a` is session-only, `d` leaves undecided (re-prompts next run), `?` shows per-option help and re-prompts. The prompt embeds an inline `composer skills:trust …` recovery hint.
871+
- **Non-interactive mode**: skip with a `composer skills:trust <package>` hint (gentler than Composer's hard-fail; CI stays deterministic).
872+
- **Persistence**: `extra.ai-agent-skill.allow-skills` in the root `composer.json`. Atomic write via temp file + rename, with a sidecar `flock` file for cross-process safety. Glob patterns (`vendor/*`) supported via a case-sensitive matcher (Composer's `BasePackage::packageNameToRegexp()` always sets `/i`, which we deliberately don't use).
873873
- **Boundary**: only `SkillPlugin::updateAgentsMd()` — the install/update event handler — invokes `SkillTrustManager::decide()`. Discovery and reporting never prompt.
874874

875-
### Auto-seeding for upgrades
875+
### First-run policy (replaces auto-seeding)
876876

877-
To prevent a re-prompt avalanche when existing users upgrade, the plugin auto-seeds the `allow-skills` map on first run with the currently-installed `type: ai-agent-skill` packages. The user already chose to `composer require` those packages — implicit trust. Auto-seeding is a one-shot operation (skipped if the map already exists).
877+
When `extra.ai-agent-skill.allow-skills` is absent and the project has installed `type: ai-agent-skill` packages, a one-time prompt asks how to seed:
878+
879+
- `[n] None` (default, strict) — every package goes through the per-package prompt.
880+
- `[d] Direct dependencies only` — auto-trust packages your root `composer.json` directly requires; transitives still prompt.
881+
- `[a] All` — auto-trust every existing skill package. Restores the v0.1.x behaviour at the user's explicit consent.
882+
883+
Non-interactive mode defaults to `[n]` and emits a `composer skills:trust …` recovery hint per affected package, marked `(in your require)` or `(pulled in by another package)`. The map is always written (possibly empty) so this prompt fires at most once per project.
884+
885+
The earlier prototype's blanket auto-seeding was replaced after the security review flagged it HIGH: a transitive dependency the user never knowingly chose got trust by default. Deny-by-default closes that vector while still allowing a single keystroke (`a`) to keep v0.1.x behaviour for users who want it.
886+
887+
### Commands
888+
889+
The plugin registers four Composer commands:
890+
891+
| Command | Purpose | Mutates? |
892+
|---|---|---|
893+
| `composer list-skills` | List every discovered skill with trust state | No |
894+
| `composer read-skill <name>` | Print full SKILL.md content for one skill | No |
895+
| `composer skills:trust <package> [--deny\|--revoke]` | Persist a trust decision without prompting | Yes |
896+
| `composer skills:list-trust` | Show every persisted decision in the allow-skills map | No |
878897

879898
## Document History
880899

examples/database-analyzer-skill/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This is a reference implementation of an AI agent skill package for the Composer AI Agent Skill Plugin.
44

5+
> **Note:** this example uses `type: ai-agent-skill` — the *dedicated skill package* pattern. Since v0.2.0, any package can ship a skill alongside its primary purpose by adding `extra.ai-agent-skill` to its `composer.json`, regardless of `type`. See the plugin's [README "Creating Skill Packages"](../../README.md#creating-skill-packages) for both patterns. Use this dedicated pattern when the package's only purpose is shipping skills; use the library-bundled pattern when adding a skill to an existing library.
6+
57
## Package Structure
68

79
```
@@ -50,7 +52,8 @@ description: What it does and when to use it (max 1024 chars)
5052
## How It Works
5153

5254
1. **Installation**: User installs this package via Composer
53-
2. **Discovery**: Plugin detects `type: ai-agent-skill` during install/update
55+
2. **Discovery**: Plugin detects `type: ai-agent-skill` (or `extra.ai-agent-skill`) during install/update
56+
2a. **Trust prompt**: First time a new package's skills appear, the plugin asks the user to allow or deny — see the main README for details
5457
3. **Registration**: Plugin parses SKILL.md and registers in AGENTS.md
5558
4. **Usage**: AI agents discover skill via AGENTS.md XML index
5659
5. **Invocation**: Agents execute `composer read-skill database-analyzer`
@@ -228,7 +231,7 @@ composer read-skill my-skill
228231

229232
**Common Issues:**
230233

231-
1. **Skill not found**: Check `type: ai-agent-skill` in composer.json
234+
1. **Skill not found**: Check `type: ai-agent-skill` (or `extra.ai-agent-skill`) in composer.json. If the package shows in `composer list-skills` as `[pending]` or `[denied]`, run `composer skills:trust <package>` to allow it.
232235
2. **Invalid frontmatter**: Validate YAML syntax and required fields
233236
3. **Wrong name format**: Use only lowercase, numbers, and hyphens
234237
4. **Description too long**: Keep under 1024 characters

src/Trust/TrustDecision.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44

55
namespace Netresearch\ComposerAgentSkillPlugin\Trust;
66

7+
/**
8+
* The user's raw answer to a single trust prompt.
9+
*
10+
* Distinct from {@see TrustState}: TrustDecision is the four-way menu choice
11+
* (`y`/`n`/`a`/`d`) the user makes at one prompt invocation; TrustState is the
12+
* resolved trust policy carried downstream through discovery and gating. The
13+
* prompt produces a TrustDecision; the manager translates it into a persisted
14+
* map entry (or session-only entry, or no-op for Discard) and then exposes
15+
* the consequences via TrustState.
16+
*/
717
enum TrustDecision: string
818
{
919
case Allow = 'allow';

0 commit comments

Comments
 (0)