## Problem Statement Extensions today can only formally declare **commands** (plus `config`, `hooks`, `events`) under `provides` — see `ExtensionManifest` (`src/specify_cli/extensions/__init__.py:606-621`) and the validation requiring "at least one command, hook, or event" (`:386-387`). There is no `provides.templates` or `provides.scripts`. An extension *can* still ship `templates/<name>.md` or `scripts/<name>.sh` files, which the resolver picks up by **convention** (`PresetResolver.collect_all_layers` → `_find_in_subdirs`, `src/specify_cli/presets/__init__.py:5417-5455`). But those contributions are: - **Undeclared** — no `id`, `name`, `description`; matched purely by filename. - **Forced to `replace`** (`:5452`) — no author control, and no way to make the intent explicit. - **Invisible to tooling** — not counted or shown by any `list`/`info` command. This blocks a consumer (e.g. a setup wizard) from enumerating extension-contributed templates/scripts with real metadata, and it means presets and extensions have asymmetric manifest schemas for the same kinds of artifacts. ## Proposed Solution Add **opt-in, declarative** `provides.templates` and `provides.scripts` sections to the extension manifest, mirroring the preset shape (`provides.templates` with `type/name/file/description`, `src/specify_cli/presets/__init__.py:400-453`) — **minus any authorable `strategy`**, since extension contributions are always `replace` (see below). ```yaml provides: commands: - name: speckit.myext.report file: commands/speckit.myext.report.md description: "..." templates: - name: myext-template file: templates/myext-template.md description: "Report scaffold contributed by myext" scripts: - name: myext-collect file: scripts/bash/myext-collect.sh description: "Data-collection helper" runtimes: [bash, powershell, python] # declared, not inferred ``` Behavior: - **Declared entries are authoritative; convention lookup remains the fallback** — same pattern presets already use (`:5380-5383`), so nothing breaks for existing extensions. - New `ExtensionManifest.templates` / `ExtensionManifest.scripts` properties + validation (shape, path-safety via `relative_extension_path_violation`, name format), consistent with the existing command/preset validators. - Declared templates/scripts become countable and describable, so downstream `list --json` / `info --json` (tracked separately) can report per-kind `provides` counts and per-artifact detail. ### Strategy: replace-only, not authorable Extension-contributed templates/scripts are **always `replace`**, and `strategy` is **not an authorable field** for extensions: - This matches the resolver's current forced-`replace` behavior (`:5452`) — zero behavior change, just making the implicit rule explicit. - The dominant extension case is contributing a **brand-new artifact** nothing else provides (e.g. `speckit.git.feature`), where `replace` simply means "become the base layer." - Augmenting an existing artifact (`wrap/prepend/append`) is deliberately **preset territory** — presets are the "customize/compose what already exists" layer; extensions are the "add new capabilities" layer. Keeping composition strategies out of extensions preserves that split. - Rather than accept a `strategy` key constrained to a single value (which invites authors to try `wrap` and hit a validation error), the manifest should **ignore/reject** any `strategy` key on extension templates/scripts and document the replace semantics. ## Alternatives Considered - **Keep convention-only lookup.** Rejected: no metadata, no counts, `replace`-only by accident, asymmetric with presets. - **Give extensions the full preset strategy set** (`wrap/prepend/append`). Rejected — augmentation is preset territory; extensions stay additive/`replace` to preserve the "presets sit above extensions" layering. - **Accept an authorable `strategy` field constrained to `replace`.** Rejected — a one-value field misleads authors; better to omit it entirely and document the rule. - **Infer script runtimes from file extensions on disk.** Workable but lossy; an explicit `runtimes` list is clearer. ## Component Specify CLI (initialization, commands) ## Use Cases 1. A wizard/UI enumerates everything an installed extension contributes — commands **and** templates **and** scripts — with names, descriptions, and (for scripts) runtimes, read straight from the manifest. 2. An extension author wants their shipped template/script to appear in `extension info` with a real description instead of being an undeclared file. 3. Tooling reports accurate per-kind `provides` counts for extensions (`{ commands, templates, scripts, hooks }`). ## Acceptance Criteria - [ ] Extension manifest schema accepts optional `provides.templates` and `provides.scripts` with `name`/`file`/`description`, and `runtimes` for scripts. - [ ] `ExtensionManifest` exposes `templates` and `scripts` properties with validation parity to existing command/preset validation (shape, relative-path safety, name format, `runtimes` type-check). - [ ] **Replace-only, `strategy` not authorable:** the schema does not accept a `strategy` field on extension templates/scripts; a present `strategy` key is ignored or rejected with a clear message. Resolution continues to treat extension layers as `replace` (`:5452`). - [ ] Declared entries are authoritative in resolution; undeclared on-disk files still resolve via convention (no regression). - [ ] Decision recorded: whether an extension may provide **only** templates/scripts (i.e. whether the "at least one command/hook/event" rule at `:386-387` is relaxed). - [ ] `schema_version` impact decided (additive under `1.0` vs. bump to `1.1`). - [ ] Tests: manifest validation (valid + malformed, including a rejected/ignored `strategy` key), resolver authoritative-vs-convention precedence, runtimes parsing. - [ ] Docs updated: `AGENTS.md` and `extensions/EXTENSION-API-REFERENCE.md` (manifest schema section), including the replace-only rule. ## Additional Context - Related wizard-data assessment: this closes the "extensions can't declare templates/scripts" gap and the `runtimes` half of the script-metadata gap. - Downstream dependents (separate issues): `--json` for `extension list`/`info` and per-kind `provides` counts; the `specify artifact` composition command benefits from the added provenance.